org.http4s.HttpRoutes Scala Examples
The following examples show how to use org.http4s.HttpRoutes.
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 |
// 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: MockHttpServer.scala From cornichon with Apache License 2.0 | 6 votes |
package com.github.agourlay.cornichon.http.server import java.net.NetworkInterface import com.github.agourlay.cornichon.core.CornichonError import monix.eval.Task import monix.execution.Scheduler import org.http4s.HttpRoutes import org.http4s.server.Router import org.http4s.server.blaze.BlazeServerBuilder import org.http4s.implicits._ import scala.jdk.CollectionConverters._ import scala.concurrent.duration._ import scala.util.Random class MockHttpServer[A](interface: Option[String], port: Option[Range], mockService: HttpRoutes[Task], maxRetries: Int = 5)(useFromAddress: String => Task[A])(implicit scheduler: Scheduler) { private val selectedInterface = interface.getOrElse(bestInterface()) private val randomPortOrder = port.fold(0 :: Nil)(r => Random.shuffle(r.toList)) private val mockRouter = Router("/" -> mockService).orNotFound def useServer(): Task[A] = if (randomPortOrder.isEmpty) Task.raiseError(MockHttpServerError.toException) else startServerTryPorts(randomPortOrder) private def startServerTryPorts(ports: List[Int], retry: Int = 0): Task[A] = startBlazeServer(ports.head).onErrorHandleWith { case _: java.net.BindException if ports.length > 1 => startServerTryPorts(ports.tail, retry) case _: java.net.BindException if retry < maxRetries => val sleepFor = retry + 1 println(s"Could not start server on any port. Retrying in $sleepFor seconds...") startServerTryPorts(randomPortOrder, retry = retry + 1).delayExecution(sleepFor.seconds) } private def startBlazeServer(port: Int): Task[A] = BlazeServerBuilder[Task](executionContext = scheduler) .bindHttp(port, selectedInterface) .withoutBanner .withHttpApp(mockRouter) .withNio2(true) .resource .use(server => useFromAddress(s"http://${server.address.getHostString}:${server.address.getPort}")) private def bestInterface(): String = NetworkInterface.getNetworkInterfaces.asScala .filter(_.isUp) .flatMap(_.getInetAddresses.asScala) .find(_.isSiteLocalAddress) .map(_.getHostAddress) .getOrElse("localhost") } case object MockHttpServerError extends CornichonError { val baseErrorMessage = "the range of ports provided for the HTTP mock is invalid" }
Example 3
Source File: RedocHttp4s.scala From tapir with Apache License 2.0 | 5 votes |
package sttp.tapir.redoc.http4s import cats.effect.{ContextShift, Sync} import org.http4s.dsl.Http4sDsl import org.http4s.headers._ import org.http4s.{Charset, HttpRoutes, MediaType} import scala.io.Source class RedocHttp4s(title: String, yaml: String, yamlName: String = "docs.yaml") { private lazy val html = { val fileName = "redoc.html" val is = getClass.getClassLoader.getResourceAsStream(fileName) assert(Option(is).nonEmpty, s"Could not find file ${fileName} on classpath.") val rawHtml = Source.fromInputStream(is).mkString // very poor man's templating engine rawHtml.replaceAllLiterally("{{docsPath}}", yamlName).replaceAllLiterally("{{title}}", title) } def routes[F[_]: ContextShift: Sync]: HttpRoutes[F] = { val dsl = Http4sDsl[F] import dsl._ HttpRoutes.of[F] { case req @ GET -> Root if req.pathInfo.endsWith("/") => Ok(html, `Content-Type`(MediaType.text.html, Charset.`UTF-8`)) // as the url to the yaml file is relative, it is important that there is a trailing slash case req @ GET -> Root => val uri = req.uri PermanentRedirect(Location(uri.withPath(uri.path.concat("/")))) case GET -> Root / `yamlName` => Ok(yaml, `Content-Type`(MediaType.text.yaml, Charset.`UTF-8`)) } } }
Example 4
Source File: BookingRoutes.scala From ticket-booking-aecor with Apache License 2.0 | 5 votes |
package ru.pavkin.booking.booking.endpoint import cats.effect.Effect import cats.implicits._ import io.circe.syntax._ import org.http4s.HttpRoutes import org.http4s.circe._ import org.http4s.dsl.Http4sDsl import ru.pavkin.booking.common.models.{BookingKey, ClientId} final class BookingRoutes[F[_]: Effect](ops: BookingEndpoint[F]) extends Http4sDsl[F] { implicit val placeBookingDecoder = jsonOf[F, PlaceBookingRequest] implicit val cancelBookingDecoder = jsonOf[F, CancelBookingRequest] private val placeBooking: HttpRoutes[F] = HttpRoutes.of { case r @ POST -> Root / userId / "bookings" => r.as[PlaceBookingRequest] .flatMap( r => ops.placeBooking(ClientId(userId), r.concertId, r.seats).flatMap { case Left(err) => BadRequest(err.toString) case Right(_) => Ok() } ) } private val cancelBooking: HttpRoutes[F] = HttpRoutes.of { case r @ DELETE -> Root / userId / "bookings" / bookingId => r.as[CancelBookingRequest] .flatMap( r => ops.cancelBooking(ClientId(userId), BookingKey(bookingId), r.reason).flatMap { case Left(err) => BadRequest(err.toString) case Right(_) => Ok() } ) } private val clientBookings: HttpRoutes[F] = HttpRoutes.of { case GET -> Root / userId / "bookings" => ops.clientBookings(ClientId(userId)).flatMap { bookings => Ok(bookings.asJson) } } val routes: HttpRoutes[F] = placeBooking <+> clientBookings <+> cancelBooking }
Example 5
Source File: EndpointWirings.scala From ticket-booking-aecor with Apache License 2.0 | 5 votes |
package ru.pavkin.booking import cats.effect.{ConcurrentEffect, Timer} import org.http4s.HttpRoutes import org.http4s.implicits._ import org.http4s.server.Router import org.http4s.server.blaze.BlazeServerBuilder import ru.pavkin.booking.booking.endpoint.{BookingRoutes, DefaultBookingEndpoint} import ru.pavkin.booking.config.HttpServer import scala.concurrent.duration.{Duration => _} final class EndpointWirings[F[_] : ConcurrentEffect : Timer]( httpServer: HttpServer, postgresWirings: PostgresWirings[F], entityWirings: EntityWirings[F]) { import entityWirings._ import postgresWirings._ val bookingsEndpoint = new DefaultBookingEndpoint(bookings, bookingViewRepo) val bookingRoutes = new BookingRoutes(bookingsEndpoint) val routes: HttpRoutes[F] = bookingRoutes.routes def launchHttpService: F[Unit] = BlazeServerBuilder[F] .bindHttp(httpServer.port, httpServer.interface) .withHttpApp(Router("/" -> routes).orNotFound) .serve .compile .drain }
Example 6
Source File: Http4sTextPlainTest.scala From guardrail with MIT License | 5 votes |
package generators.Http4s.Client.contentType import _root_.tests.contentTypes.textPlain.client.http4s.foo.FooClient import _root_.tests.contentTypes.textPlain.client.{ http4s => cdefs } import _root_.tests.contentTypes.textPlain.server.http4s.foo.{ DoBarResponse, DoBazResponse, DoFooResponse, FooHandler, FooResource } import _root_.tests.contentTypes.textPlain.server.{ http4s => sdefs } import org.scalatest.{ EitherValues, FunSuite, Matchers } import org.http4s.dsl.io._ import org.http4s.headers._ import cats.effect.IO import org.http4s.client.Client import org.http4s.{ Charset, HttpRoutes, MediaType } class Http4sTextPlainTest extends FunSuite with Matchers with EitherValues { import org.http4s.implicits._ test("Plain text should be emitted for required parameters (raw)") { val route: HttpRoutes[IO] = HttpRoutes.of { case req @ POST -> Root / "foo" => if (req.contentType.contains(`Content-Type`(MediaType.text.plain, Charset.`UTF-8`))) { for { value <- req.as[String] resp <- if (value == "sample") Created() else NotAcceptable() } yield resp } else NotAcceptable() } val client: Client[IO] = Client.fromHttpApp(route.orNotFound) val fooClient = FooClient.httpClient(client) fooClient.doFoo("sample").attempt.unsafeRunSync().right.value shouldBe cdefs.foo.DoFooResponse.Created } test("Plain text should be emitted for optional parameters (raw)") { val route: HttpRoutes[IO] = HttpRoutes.of { case req @ POST -> Root / "bar" => if (req.contentType.contains(`Content-Type`(MediaType.text.plain, Charset.`UTF-8`))) { for { value <- req.as[String] resp <- if (value == "sample") Created() else NotAcceptable() } yield resp } else NotAcceptable() } val client: Client[IO] = Client.fromHttpApp(route.orNotFound) val fooClient = FooClient.httpClient(client) fooClient.doBar(Some("sample")).attempt.unsafeRunSync().right.value shouldBe cdefs.foo.DoBarResponse.Created } test("Plain text should be emitted for required parameters") { val route: HttpRoutes[IO] = new FooResource[IO]().routes(new FooHandler[IO] { def doFoo(respond: DoFooResponse.type)(body: String): IO[sdefs.foo.DoFooResponse] = if (body == "sample") { IO.pure(respond.Created) } else { IO.pure(respond.NotAcceptable) } def doBar(respond: DoBarResponse.type)(body: Option[String]): IO[sdefs.foo.DoBarResponse] = ??? def doBaz(respond: DoBazResponse.type)(body: Option[String]): IO[sdefs.foo.DoBazResponse] = ??? }) val client: Client[IO] = Client.fromHttpApp(route.orNotFound) val fooClient = FooClient.httpClient(client) fooClient.doFoo("sample").attempt.unsafeRunSync().right.value shouldBe cdefs.foo.DoFooResponse.Created } test("Plain text should be emitted for present optional parameters") { val route: HttpRoutes[IO] = new FooResource[IO]().routes(new FooHandler[IO] { def doFoo(respond: DoFooResponse.type)(body: String): IO[sdefs.foo.DoFooResponse] = ??? def doBar(respond: DoBarResponse.type)(body: Option[String]): IO[sdefs.foo.DoBarResponse] = if (body.contains("sample")) { IO.pure(respond.Created) } else { IO.pure(respond.NotAcceptable) } def doBaz(respond: DoBazResponse.type)(body: Option[String]): IO[sdefs.foo.DoBazResponse] = ??? }) val client: Client[IO] = Client.fromHttpApp(route.orNotFound) val fooClient = FooClient.httpClient(client) fooClient.doBar(Some("sample")).attempt.unsafeRunSync().right.value shouldBe cdefs.foo.DoBarResponse.Created } test("Plain text should be emitted for missing optional parameters") { val route: HttpRoutes[IO] = new FooResource[IO]().routes(new FooHandler[IO] { def doFoo(respond: DoFooResponse.type)(body: String): IO[sdefs.foo.DoFooResponse] = ??? def doBar(respond: DoBarResponse.type)(body: Option[String]): IO[sdefs.foo.DoBarResponse] = if (body.isEmpty) { IO.pure(respond.Created) } else { IO.pure(respond.NotAcceptable) } def doBaz(respond: DoBazResponse.type)(body: Option[String]): IO[sdefs.foo.DoBazResponse] = ??? }) val client: Client[IO] = Client.fromHttpApp(route.orNotFound) val fooClient = FooClient.httpClient(client) fooClient.doBar(None).attempt.unsafeRunSync().right.value shouldBe cdefs.foo.DoBarResponse.Created } }
Example 7
Source File: Http4sFullTracerTest.scala From guardrail with MIT License | 5 votes |
package core.Http4s import _root_.tracer.client.{ http4s => cdefs } import _root_.tracer.server.http4s.addresses.{ AddressesHandler, AddressesResource, GetAddressResponse, GetAddressesResponse } import _root_.tracer.server.http4s.users.{ GetUserResponse, UsersHandler, UsersResource } import _root_.tracer.server.{ http4s => sdefs } import _root_.tracer.client.http4s.users.UsersClient import _root_.tracer.client.http4s.addresses.AddressesClient import _root_.tracer.server.http4s.Http4sImplicits.TraceBuilder import cats.effect.IO import org.http4s.{ Header, HttpRoutes, Request } import org.http4s.client.Client import org.http4s.implicits._ import org.http4s.syntax.StringSyntax import org.scalatest.{ EitherValues, FunSuite, Matchers } class Http4sFullTracerTest extends FunSuite with Matchers with EitherValues with StringSyntax { val traceHeaderKey = "tracer-label" def log(line: String): Unit = () def trace: String => Request[IO] => TraceBuilder[IO] = { name => request => // In a real environment, this would be where you could establish a new // tracing context and inject that fresh header value. log(s"Expecting all requests to have ${traceHeaderKey} header.") traceBuilder(request.headers.get(traceHeaderKey.ci).get.value) } def traceBuilder(parentValue: String): TraceBuilder[IO] = { name => httpClient => Client { req => httpClient.run(req.putHeaders(Header(traceHeaderKey, parentValue))) } } test("full tracer: passing headers through multiple levels") { // Establish the "Address" server val server2: HttpRoutes[IO] = new AddressesResource(trace).routes( new AddressesHandler[IO] { def getAddress(respond: GetAddressResponse.type)(id: String)(traceBuilder: TraceBuilder[IO]) = IO.pure(if (id == "addressId") { respond.Ok(sdefs.definitions.Address(Some("line1"), Some("line2"), Some("line3"))) } else sdefs.addresses.GetAddressResponse.NotFound) def getAddresses(respond: GetAddressesResponse.type)()(traceBuilder: TraceBuilder[IO]) = IO.pure(sdefs.addresses.GetAddressesResponse.NotFound) } ) // Establish the "User" server val server1: HttpRoutes[IO] = new UsersResource(trace).routes( new UsersHandler[IO] { // ... using the "Address" server explicitly in the addressesClient val addressesClient = AddressesClient.httpClient(Client.fromHttpApp(server2.orNotFound)) def getUser(respond: GetUserResponse.type)(id: String)(traceBuilder: TraceBuilder[IO]) = addressesClient .getAddress(traceBuilder, "addressId") .map { case cdefs.addresses.GetAddressResponse.Ok(address) => respond.Ok(sdefs.definitions.User("1234", sdefs.definitions.UserAddress(address.line1, address.line2, address.line3))) case cdefs.addresses.GetAddressResponse.NotFound => respond.NotFound } } ) // Build a UsersClient using the User server val usersClient = UsersClient.httpClient(Client.fromHttpApp(server1.orNotFound)) // As this is the entry point, we either have a tracing header from // somewhere else, or we generate one for top-level request. val testTrace = traceBuilder("top-level-request") // Make a request against the mock servers using a hard-coded user ID val retrieved: cdefs.users.GetUserResponse = usersClient.getUser(testTrace, "1234").attempt.unsafeRunSync().right.value retrieved shouldBe cdefs.users.GetUserResponse .Ok(cdefs.definitions.User("1234", cdefs.definitions.UserAddress(Some("line1"), Some("line2"), Some("line3")))) } }
Example 8
Source File: TransactionRoute.scala From aecor with MIT License | 5 votes |
package aecor.example.transaction import java.util.UUID import aecor.example.account import aecor.example.account.AccountId import aecor.example.common.Amount import cats.effect.{Effect, Sync} import cats.implicits._ import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityDecoder import org.http4s.dsl.Http4sDsl import io.circe.generic.auto._ trait TransactionService[F[_]] { def authorizePayment(transactionId: TransactionId, from: From[AccountId], to: To[AccountId], amount: Amount): F[TransactionRoute.ApiResult] } object TransactionRoute { sealed trait ApiResult object ApiResult { case object Authorized extends ApiResult case class Declined(reason: String) extends ApiResult } final case class CreateTransactionRequest(from: From[AccountId], to: To[AccountId], amount: Amount) object TransactionIdVar { def unapply(arg: String): Option[TransactionId] = TransactionId(arg).some } private final class Builder[F[_]: Sync](service: TransactionService[F]) extends Http4sDsl[F] with CirceEntityDecoder { def routes: HttpRoutes[F] = HttpRoutes.of[F] { case req @ PUT -> Root / "transactions" / TransactionIdVar(transactionId) => for { body <- req.as[CreateTransactionRequest] CreateTransactionRequest(from, to, amount) = body resp <- service.authorizePayment(transactionId, from, to, amount).flatMap { case ApiResult.Authorized => Ok("Authorized") case ApiResult.Declined(reason) => BadRequest(s"Declined: $reason") } } yield resp case POST -> Root / "test" => service .authorizePayment( TransactionId(UUID.randomUUID.toString), From(account.EventsourcedAlgebra.rootAccountId), To(AccountId("foo")), Amount(1) ) .flatMap { case ApiResult.Authorized => Ok("Authorized") case ApiResult.Declined(reason) => BadRequest(s"Declined: $reason") } } } def apply[F[_]: Effect](api: TransactionService[F]): HttpRoutes[F] = new Builder(api).routes }
Example 9
Source File: SagaEndpoint.scala From zio-saga with MIT License | 5 votes |
package com.vladkopanev.zio.saga.example.endpoint import com.vladkopanev.zio.saga.example.{ OrderSagaCoordinator, TaskC } import com.vladkopanev.zio.saga.example.model.OrderInfo import org.http4s.circe._ import org.http4s.dsl.Http4sDsl import org.http4s.implicits._ import org.http4s.{ HttpApp, HttpRoutes } import zio.interop.catz._ final class SagaEndpoint(orderSagaCoordinator: OrderSagaCoordinator) extends Http4sDsl[TaskC] { private implicit val decoder = jsonOf[TaskC, OrderInfo] val service: HttpApp[TaskC] = HttpRoutes .of[TaskC] { case req @ POST -> Root / "saga" / "finishOrder" => for { OrderInfo(userId, orderId, money, bonuses) <- req.as[OrderInfo] resp <- orderSagaCoordinator .runSaga(userId, orderId, money, bonuses, None) .foldM(fail => InternalServerError(fail.getMessage), _ => Ok("Saga submitted")) } yield resp } .orNotFound }
Example 10
Source File: AuthExampleApp.scala From caliban with Apache License 2.0 | 5 votes |
package caliban.http4s import caliban.GraphQL._ import caliban.schema.GenericSchema import caliban.{ Http4sAdapter, RootResolver } import org.http4s.HttpRoutes import org.http4s.dsl.Http4sDsl import org.http4s.implicits._ import org.http4s.server.blaze.BlazeServerBuilder import org.http4s.server.{ Router, ServiceErrorHandler } import org.http4s.util.CaseInsensitiveString import zio._ import zio.interop.catz._ import zio.interop.catz.implicits._ import scala.concurrent.ExecutionContext object AuthExampleApp extends CatsApp { // Simple service that returns the token coming from the request type Auth = Has[Auth.Service] object Auth { trait Service { def token: String } } type AuthTask[A] = RIO[Auth, A] case class MissingToken() extends Throwable // http4s middleware that extracts a token from the request and eliminate the Auth layer dependency object AuthMiddleware { def apply(route: HttpRoutes[AuthTask]): HttpRoutes[Task] = Http4sAdapter.provideLayerFromRequest( route, _.headers.get(CaseInsensitiveString("token")) match { case Some(value) => ZLayer.succeed(new Auth.Service { override def token: String = value.value }) case None => ZLayer.fail(MissingToken()) } ) } // http4s error handler to customize the response for our throwable object dsl extends Http4sDsl[Task] import dsl._ val errorHandler: ServiceErrorHandler[Task] = _ => { case MissingToken() => Forbidden() } // our GraphQL API val schema: GenericSchema[Auth] = new GenericSchema[Auth] {} import schema._ case class Query(token: RIO[Auth, String]) private val resolver = RootResolver(Query(ZIO.access[Auth](_.get[Auth.Service].token))) private val api = graphQL(resolver) override def run(args: List[String]): ZIO[ZEnv, Nothing, ExitCode] = (for { interpreter <- api.interpreter route = AuthMiddleware(Http4sAdapter.makeHttpService(interpreter)) _ <- BlazeServerBuilder[Task](ExecutionContext.global) .withServiceErrorHandler(errorHandler) .bindHttp(8088, "localhost") .withHttpApp(Router[Task]("/api/graphql" -> route).orNotFound) .resource .toManaged .useForever } yield ()).exitCode }
Example 11
Source File: StatusesService.scala From zio-telemetry with Apache License 2.0 | 5 votes |
package zio.telemetry.opentracing.example.http import io.circe.Encoder import io.opentracing.propagation.Format.Builtin.{ HTTP_HEADERS => HttpHeadersFormat } import io.opentracing.propagation.TextMapAdapter import io.opentracing.tag.Tags import org.http4s.circe.jsonEncoderOf import org.http4s.dsl.Http4sDsl import org.http4s.{ EntityEncoder, HttpRoutes } import sttp.model.Uri import zio.clock.Clock import zio.interop.catz._ import zio.telemetry.opentracing.OpenTracing import zio.UIO import zio.ZIO import zio.ZLayer import scala.collection.mutable import scala.jdk.CollectionConverters._ object StatusesService { def statuses(backendUri: Uri, service: ZLayer[Clock, Throwable, Clock with OpenTracing]): HttpRoutes[AppTask] = { val dsl: Http4sDsl[AppTask] = Http4sDsl[AppTask] import dsl._ implicit def encoder[A: Encoder]: EntityEncoder[AppTask, A] = jsonEncoderOf[AppTask, A] HttpRoutes.of[AppTask] { case GET -> Root / "statuses" => val zio = for { env <- ZIO.environment[OpenTracing] _ <- env.get.root("/statuses") _ <- OpenTracing.tag(Tags.SPAN_KIND.getKey, Tags.SPAN_KIND_CLIENT) _ <- OpenTracing.tag(Tags.HTTP_METHOD.getKey, GET.name) _ <- OpenTracing.setBaggageItem("proxy-baggage-item-key", "proxy-baggage-item-value") buffer <- UIO.succeed(new TextMapAdapter(mutable.Map.empty[String, String].asJava)) _ <- OpenTracing.inject(HttpHeadersFormat, buffer) headers <- extractHeaders(buffer) up = Status.up("proxy") res <- Client .status(backendUri.path("status"), headers) .map(_.body) .flatMap { case Right(s) => Ok(Statuses(List(s, up))) case _ => Ok(Statuses(List(Status.down("backend"), up))) } } yield res zio.provideLayer(service) } } private def extractHeaders(adapter: TextMapAdapter): UIO[Map[String, String]] = { val m = mutable.Map.empty[String, String] UIO(adapter.forEach { entry => m.put(entry.getKey, entry.getValue) () }).as(m.toMap) } }
Example 12
Source File: StatusesService.scala From zio-telemetry with Apache License 2.0 | 5 votes |
package zio.telemetry.opentelemetry.example.http import io.circe.Encoder import io.opentelemetry.OpenTelemetry import io.opentelemetry.context.propagation.HttpTextFormat.Setter import io.opentelemetry.trace.{ Span, Status => TraceStatus } import org.http4s.circe.jsonEncoderOf import org.http4s.dsl.Http4sDsl import org.http4s.{ EntityEncoder, HttpRoutes } import zio.UIO import zio.interop.catz._ import zio.telemetry.opentelemetry.Tracing.root import zio.telemetry.opentelemetry.attributevalue.AttributeValueConverterInstances._ import zio.telemetry.opentelemetry.Tracing import scala.collection.mutable object StatusesService { val dsl: Http4sDsl[AppTask] = Http4sDsl[AppTask] import dsl._ implicit def encoder[A: Encoder]: EntityEncoder[AppTask, A] = jsonEncoderOf[AppTask, A] val httpTextFormat = OpenTelemetry.getPropagators.getHttpTextFormat val setter: Setter[mutable.Map[String, String]] = (carrier, key, value) => carrier.update(key, value) val errorMapper: PartialFunction[Throwable, TraceStatus] = { case _ => TraceStatus.UNKNOWN } val routes: HttpRoutes[AppTask] = HttpRoutes.of[AppTask] { case GET -> Root / "statuses" => root("/statuses", Span.Kind.SERVER, errorMapper) { for { carrier <- UIO(mutable.Map[String, String]().empty) _ <- Tracing.setAttribute("http.method", "get") _ <- Tracing.addEvent("proxy-event") _ <- Tracing.inject(httpTextFormat, carrier, setter) res <- Client.status(carrier.toMap).flatMap(Ok(_)) } yield res } } }
Example 13
Source File: BidderHttpAppBuilder.scala From scala-openrtb with Apache License 2.0 | 5 votes |
package com.powerspace.openrtb.examples.rtb.http4s.bidder import com.google.openrtb.BidRequest import com.powerspace.openrtb.examples.rtb.http4s.common.ExampleSerdeModule import io.circe.Decoder import monix.eval.Task import org.http4s.dsl.Http4sDsl import org.http4s.{EntityDecoder, HttpApp} object BidderHttpAppBuilder { private val bidder = new Bidder[Task] private val dsl = Http4sDsl[Task] private val serdeModule = ExampleSerdeModule private def handleBid(bidRequest: BidRequest) = { import dsl._ import org.http4s.circe._ bidder .bidOn(bidRequest) .flatMap { case Some(bidResponse) => // encode the bidResponse to a json object as part of the http response body Ok(serdeModule.bidResponseEncoder(bidResponse)) case None => Ok() } } }
Example 14
Source File: HealthCheckRoutes.scala From http4s-poc-api with MIT License | 5 votes |
package server import cats.effect.Sync import log.effect.LogWriter import model.DomainModel._ import org.http4s.dsl.Http4sDsl import org.http4s.{EntityEncoder, HttpRoutes, Method} import cats.syntax.flatMap._ sealed abstract class HealthCheckRoutes[F[_]: Sync]( implicit responseEncoder: EntityEncoder[F, ServiceSignature] ) extends Http4sDsl[F] { def make(log: LogWriter[F]): HttpRoutes[F] = HttpRoutes.of[F] { case Method.GET -> Root => log.debug(s"Serving HealthCheck request") >> Ok(serviceSignature) } private val serviceSignature = ServiceSignature( name = BuildInfo.name, version = BuildInfo.version, scalaVersion = BuildInfo.scalaVersion, scalaOrganization = BuildInfo.scalaOrganization, buildTime = BuildInfo.buildTime ) } object HealthCheckRoutes { def apply[F[_]: Sync: EntityEncoder[*[_], ServiceSignature]]: HealthCheckRoutes[F] = new HealthCheckRoutes[F] {} }
Example 15
Source File: PriceRoutes.scala From http4s-poc-api with MIT License | 5 votes |
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 16
Source File: HealthCheckHttpApiTests.scala From http4s-poc-api with MIT License | 5 votes |
import cats.instances.string._ import cats.syntax.apply._ import io.circe.generic.auto._ import io.circe.{Decoder, Encoder} import log.effect.zio.ZioLogWriter.consoleLog import model.DomainModel.ServiceSignature import org.http4s.circe.{jsonEncoderOf, jsonOf} import org.http4s.{HttpRoutes, Request, Status} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import server.HealthCheckRoutes import syntax.http4sService._ import syntax.responseVerification._ import zio.Task import zio.interop.catz._ final class HealthCheckHttpApiTests extends AnyFlatSpec with Matchers with Fixtures { implicit def testEncoder[A: Encoder] = jsonEncoderOf[Task, A] implicit def testDecoder[A: Decoder] = jsonOf[Task, A] it should "respond with Ok status 200 and the correct service signature" in { val httpApi: HttpRoutes[Task] = HealthCheckRoutes[Task].make(consoleLog) val verified = httpApi .runFor(Request[Task]()) .verify[ServiceSignature]( Status.Ok, sign => ( sign.name isSameAs "http4s-poc-api", sign.version isNotSameAs "", sign.scalaVersion isSameAs "2.13.2", sign.scalaOrganization isSameAs "org.scala-lang" ).mapN((_, _, _, _) => sign) ) assertOn(verified) } }
Example 17
Source File: TestRoutes.scala From scala-server-lambda with MIT License | 5 votes |
package io.github.howardjohn.lambda.http4s import cats.{Applicative, MonadError} import cats.effect.Sync import cats.implicits._ import io.github.howardjohn.lambda.LambdaHandlerBehavior import io.github.howardjohn.lambda.LambdaHandlerBehavior._ import org.http4s.dsl.Http4sDsl import org.http4s.{EntityDecoder, Header, HttpRoutes} import org.http4s.circe._ import io.circe.generic.auto._ import io.circe.syntax._ import org.http4s.dsl.impl.OptionalQueryParamDecoderMatcher class TestRoutes[F[_]] { object TimesQueryMatcher extends OptionalQueryParamDecoderMatcher[Int]("times") val dsl = Http4sDsl[F] import dsl._ def routes(implicit sync: Sync[F], jsonDecoder: EntityDecoder[F, JsonBody], me: MonadError[F, Throwable], stringDecoder: EntityDecoder[F, String], ap: Applicative[F]): HttpRoutes[F] = HttpRoutes.of[F] { case GET -> Root / "hello" :? TimesQueryMatcher(times) => Ok { Seq .fill(times.getOrElse(1))("Hello World!") .mkString(" ") } case GET -> Root / "long" => Applicative[F].pure(Thread.sleep(1000)).flatMap(_ => Ok("Hello World!")) case GET -> Root / "exception" => throw RouteException() case GET -> Root / "error" => InternalServerError() case req@GET -> Root / "header" => val header = req.headers.find(h => h.name.value == inputHeader).map(_.value).getOrElse("Header Not Found") Ok(header, Header(outputHeader, outputHeaderValue)) case req@POST -> Root / "post" => req.as[String].flatMap(s => Ok(s)) case req@POST -> Root / "json" => req.as[JsonBody].flatMap(s => Ok(LambdaHandlerBehavior.jsonReturn.asJson)) } }
Example 18
Source File: SwaggerHttp4s.scala From tapir with Apache License 2.0 | 5 votes |
package sttp.tapir.swagger.http4s import java.util.Properties import cats.effect.{Blocker, ContextShift, Sync} import org.http4s.{HttpRoutes, StaticFile, Uri} import org.http4s.dsl.Http4sDsl import org.http4s.headers.Location import scala.concurrent.ExecutionContext class SwaggerHttp4s( yaml: String, contextPath: String = "docs", yamlName: String = "docs.yaml", redirectQuery: Map[String, Seq[String]] = Map.empty ) { private val swaggerVersion = { val p = new Properties() val pomProperties = getClass.getResourceAsStream("/META-INF/maven/org.webjars/swagger-ui/pom.properties") try p.load(pomProperties) finally pomProperties.close() p.getProperty("version") } def routes[F[_]: ContextShift: Sync]: HttpRoutes[F] = { val dsl = Http4sDsl[F] import dsl._ HttpRoutes.of[F] { case path @ GET -> Root / `contextPath` => val queryParameters = Map("url" -> Seq(s"${path.uri}/$yamlName")) ++ redirectQuery Uri .fromString(s"${path.uri}/index.html") .map(uri => uri.setQueryParams(queryParameters)) .map(uri => PermanentRedirect(Location(uri))) .getOrElse(NotFound()) case GET -> Root / `contextPath` / `yamlName` => Ok(yaml) case GET -> Root / `contextPath` / swaggerResource => StaticFile .fromResource( s"/META-INF/resources/webjars/swagger-ui/$swaggerVersion/$swaggerResource", Blocker.liftExecutionContext(ExecutionContext.global) ) .getOrElseF(NotFound()) } } }
Example 19
Source File: MultipleEndpointsDocumentationHttp4sServer.scala From tapir with Apache License 2.0 | 5 votes |
package sttp.tapir.examples import java.util.concurrent.atomic.AtomicReference import cats.effect._ import cats.implicits._ import com.github.ghik.silencer.silent import io.circe.generic.auto._ import org.http4s.HttpRoutes import org.http4s.server.Router import org.http4s.server.blaze.BlazeServerBuilder import org.http4s.syntax.kleisli._ import sttp.tapir._ import sttp.tapir.docs.openapi._ import sttp.tapir.json.circe._ import sttp.tapir.openapi.OpenAPI import sttp.tapir.openapi.circe.yaml._ import sttp.tapir.server.http4s._ import sttp.tapir.swagger.http4s.SwaggerHttp4s import scala.concurrent.ExecutionContext object MultipleEndpointsDocumentationHttp4sServer extends App { // endpoint descriptions case class Author(name: String) case class Book(title: String, year: Int, author: Author) val booksListing: Endpoint[Unit, Unit, Vector[Book], Nothing] = endpoint.get .in("books") .in("list" / "all") .out(jsonBody[Vector[Book]]) val addBook: Endpoint[Book, Unit, Unit, Nothing] = endpoint.post .in("books") .in("add") .in( jsonBody[Book] .description("The book to add") .example(Book("Pride and Prejudice", 1813, Author("Jane Austen"))) ) // server-side logic 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) val books = new AtomicReference( Vector( Book("The Sorrows of Young Werther", 1774, Author("Johann Wolfgang von Goethe")), Book("Iliad", -8000, Author("Homer")), Book("Nad Niemnem", 1888, Author("Eliza Orzeszkowa")), Book("The Colour of Magic", 1983, Author("Terry Pratchett")), Book("The Art of Computer Programming", 1968, Author("Donald Knuth")), Book("Pharaoh", 1897, Author("Boleslaw Prus")) ) ) val booksListingRoutes: HttpRoutes[IO] = booksListing.toRoutes(_ => IO(books.get().asRight[Unit])) @silent("discarded") val addBookRoutes: HttpRoutes[IO] = addBook.toRoutes(book => IO((books.getAndUpdate(books => books :+ book): Unit).asRight[Unit])) val routes: HttpRoutes[IO] = booksListingRoutes <+> addBookRoutes // generating the documentation in yml; extension methods come from imported packages val openApiDocs: OpenAPI = List(booksListing, addBook).toOpenAPI("The tapir library", "1.0.0") val openApiYml: String = openApiDocs.toYaml // starting the server BlazeServerBuilder[IO](ec) .bindHttp(8080, "localhost") .withHttpApp(Router("/" -> (routes <+> new SwaggerHttp4s(openApiYml).routes[IO])).orNotFound) .resource .use { _ => IO { println("Go to: http://localhost:8080/docs") println("Press any key to exit ...") scala.io.StdIn.readLine() } } .unsafeRunSync() }
Example 20
Source File: StreamingHttp4sFs2Server.scala From tapir with Apache License 2.0 | 5 votes |
package sttp.tapir.examples import java.nio.charset.StandardCharsets import cats.effect._ import cats.implicits._ import org.http4s.HttpRoutes import org.http4s.server.Router import org.http4s.server.blaze.BlazeServerBuilder import org.http4s.syntax.kleisli._ import sttp.client._ import sttp.tapir._ import sttp.tapir.server.http4s._ import fs2._ import sttp.model.HeaderNames import scala.concurrent.ExecutionContext import scala.concurrent.duration._ // https://github.com/softwaremill/tapir/issues/367 object StreamingHttp4sFs2Server extends App { // corresponds to: GET /receive?name=... // We need to provide both the schema of the value (for documentation), as well as the format (media type) of the // body. Here, the schema is a `string` and the media type is `text/plain`. val streamingEndpoint = endpoint.get .in("receive") .out(header[Long](HeaderNames.ContentLength)) .out(streamBody[Stream[IO, Byte]](schemaFor[String], CodecFormat.TextPlain(), Some(StandardCharsets.UTF_8))) // mandatory implicits 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) // converting an endpoint to a route (providing server-side logic); extension method comes from imported packages val streamingRoutes: HttpRoutes[IO] = streamingEndpoint.toRoutes { _ => val size = 100L Stream .emit(List[Char]('a', 'b', 'c', 'd')) .repeat .flatMap(list => Stream.chunk(Chunk.seq(list))) .metered[IO](100.millis) .take(size) .covary[IO] .map(_.toByte) .pure[IO] .map(s => Right((size, s))) } // starting the server BlazeServerBuilder[IO](ec) .bindHttp(8080, "localhost") .withHttpApp(Router("/" -> streamingRoutes).orNotFound) .resource .use { _ => IO { implicit val backend: SttpBackend[Identity, Nothing, NothingT] = HttpURLConnectionBackend() val result: String = basicRequest.response(asStringAlways).get(uri"http://localhost:8080/receive").send().body println("Got result: " + result) assert(result == "abcd" * 25) } } .unsafeRunSync() }
Example 21
Source File: HelloWorldHttp4sServer.scala From tapir with Apache License 2.0 | 5 votes |
package sttp.tapir.examples import cats.effect._ import sttp.client._ import org.http4s.HttpRoutes import org.http4s.server.Router import org.http4s.server.blaze.BlazeServerBuilder import org.http4s.syntax.kleisli._ import sttp.tapir._ import sttp.tapir.server.http4s._ import cats.implicits._ import scala.concurrent.ExecutionContext object HelloWorldHttp4sServer extends App { // the endpoint: single fixed path input ("hello"), single query parameter // corresponds to: GET /hello?name=... val helloWorld: Endpoint[String, Unit, String, Nothing] = endpoint.get.in("hello").in(query[String]("name")).out(stringBody) // mandatory implicits 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) // converting an endpoint to a route (providing server-side logic); extension method comes from imported packages val helloWorldRoutes: HttpRoutes[IO] = helloWorld.toRoutes(name => IO(s"Hello, $name!".asRight[Unit])) // starting the server BlazeServerBuilder[IO](ec) .bindHttp(8080, "localhost") .withHttpApp(Router("/" -> helloWorldRoutes).orNotFound) .resource .use { _ => IO { implicit val backend: SttpBackend[Identity, Nothing, NothingT] = HttpURLConnectionBackend() val result: String = basicRequest.response(asStringAlways).get(uri"http://localhost:8080/hello?name=Frodo").send().body println("Got result: " + result) assert(result == "Hello, Frodo!") } } .unsafeRunSync() }
Example 22
Source File: package.scala From tapir with Apache License 2.0 | 5 votes |
package sttp.tapir.server.http4s import org.http4s.HttpRoutes import sttp.tapir.server.ServerEndpoint import sttp.tapir.ztapir._ import zio.interop.catz._ import zio.{Task, URIO, ZIO} package object ztapir { implicit class RichZEndpointRoutes[I, E, O](e: ZEndpoint[I, E, O]) { def toRoutes(logic: I => ZIO[Any, E, O])(implicit serverOptions: Http4sServerOptions[Task]): HttpRoutes[Task] = e.zServerLogic(logic).toRoutes def toRoutesR[R](logic: I => ZIO[R, E, O])(implicit serverOptions: Http4sServerOptions[Task]): URIO[R, HttpRoutes[Task]] = e.zServerLogic(logic).toRoutesR } implicit class RichZServerEndpointRoutes[I, E, O](se: ZServerEndpoint[Any, I, E, O]) { def toRoutes(implicit serverOptions: Http4sServerOptions[Task]): HttpRoutes[Task] = List(se).toRoutes } implicit class RichZServerEndpointsRoutes[I, E, O](serverEndpoints: List[ZServerEndpoint[Any, _, _, _]]) { def toRoutes(implicit serverOptions: Http4sServerOptions[Task]): HttpRoutes[Task] = { new EndpointToHttp4sServer(serverOptions).toRoutes(serverEndpoints) } } implicit class RichZServerEndpointRRoutes[R, I, E, O](se: ZServerEndpoint[R, I, E, O]) { def toRoutesR(implicit serverOptions: Http4sServerOptions[Task]): URIO[R, HttpRoutes[Task]] = List(se).toRoutesR } implicit class RichZServerEndpointsRRoutes[R](serverEndpoints: List[ZServerEndpoint[R, _, _, _]]) { def toRoutesR(implicit serverOptions: Http4sServerOptions[Task]): URIO[R, HttpRoutes[Task]] = URIO.access[R] { env => val taskServerEndpoints = serverEndpoints.map(toTaskEndpointR(env, _)) new EndpointToHttp4sServer(serverOptions).toRoutes(taskServerEndpoints) } } private def toTaskEndpointR[R, I, E, O](env: R, se: ZServerEndpoint[R, I, E, O]): ServerEndpoint[I, E, O, Nothing, Task] = { ServerEndpoint( se.endpoint, _ => (i: I) => se.logic(new ZIOMonadError[R])(i).provide(env) ) } }
Example 23
Source File: EndpointToHttp4sServer.scala From tapir with Apache License 2.0 | 5 votes |
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 24
Source File: TapirHttp4sServer.scala From tapir with Apache License 2.0 | 5 votes |
package sttp.tapir.server.http4s import cats.Monad import cats.effect.{ContextShift, Sync} import cats.implicits._ import org.http4s.{EntityBody, HttpRoutes} import sttp.tapir.Endpoint import sttp.tapir.Endpoint import sttp.tapir.server.ServerEndpoint import sttp.tapir.typelevel.ReplaceFirstInTuple import scala.reflect.ClassTag trait TapirHttp4sServer { implicit class RichHttp4sHttpEndpoint[I, E, O, F[_]](e: Endpoint[I, E, O, EntityBody[F]]) { def toRoutes( logic: I => F[Either[E, O]] )(implicit serverOptions: Http4sServerOptions[F], fs: Sync[F], fcs: ContextShift[F]): HttpRoutes[F] = { new EndpointToHttp4sServer(serverOptions).toRoutes(e.serverLogic(logic)) } def toRouteRecoverErrors(logic: I => F[O])(implicit serverOptions: Http4sServerOptions[F], fs: Sync[F], fcs: ContextShift[F], eIsThrowable: E <:< Throwable, eClassTag: ClassTag[E] ): HttpRoutes[F] = { new EndpointToHttp4sServer(serverOptions).toRoutes(e.serverLogicRecoverErrors(logic)) } } implicit class RichHttp4sServerEndpoint[I, E, O, F[_]](se: ServerEndpoint[I, E, O, EntityBody[F], F]) { def toRoutes(implicit serverOptions: Http4sServerOptions[F], fs: Sync[F], fcs: ContextShift[F]): HttpRoutes[F] = new EndpointToHttp4sServer(serverOptions).toRoutes(se) } implicit class RichHttp4sServerEndpoints[F[_]](serverEndpoints: List[ServerEndpoint[_, _, _, EntityBody[F], F]]) { def toRoutes(implicit serverOptions: Http4sServerOptions[F], fs: Sync[F], fcs: ContextShift[F]): HttpRoutes[F] = { new EndpointToHttp4sServer(serverOptions).toRoutes(serverEndpoints) } } implicit class RichToMonadFunction[T, U, F[_]: Monad](a: T => F[U]) { @deprecated def andThenFirst[U_TUPLE, T_TUPLE, O]( l: U_TUPLE => F[O] )(implicit replaceFirst: ReplaceFirstInTuple[T, U, T_TUPLE, U_TUPLE]): T_TUPLE => F[O] = { tTuple => val t = replaceFirst.first(tTuple) a(t).flatMap { u => val uTuple = replaceFirst.replace(tTuple, u) l(uTuple) } } } implicit class RichToMonadOfEitherFunction[T, U, E, F[_]: Monad](a: T => F[Either[E, U]]) { @deprecated def andThenFirstE[U_TUPLE, T_TUPLE, O]( l: U_TUPLE => F[Either[E, O]] )(implicit replaceFirst: ReplaceFirstInTuple[T, U, T_TUPLE, U_TUPLE]): T_TUPLE => F[Either[E, O]] = { tTuple => val t = replaceFirst.first(tTuple) a(t).flatMap { case Left(e) => implicitly[Monad[F]].point(Left(e)) case Right(u) => val uTuple = replaceFirst.replace(tTuple, u) l(uTuple) } } } }
Example 25
Source File: Http4sServerTests.scala From tapir with Apache License 2.0 | 5 votes |
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 26
Source File: InvoicesApi.scala From event-sourcing-kafka-streams with MIT License | 5 votes |
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 27
Source File: Statics.scala From event-sourcing-kafka-streams with MIT License | 5 votes |
package org.amitayh.invoices.web import cats.effect.{ContextShift, Sync} import org.http4s.dsl.Http4sDsl import org.http4s.{HttpRoutes, StaticFile} import scala.concurrent.ExecutionContext.global class Statics[F[_]: Sync: ContextShift] extends Http4sDsl[F] { val service: HttpRoutes[F] = HttpRoutes.of[F] { case request @ GET -> fileName => StaticFile .fromResource( name = s"/statics$fileName", blockingExecutionContext = global, req = Some(request), preferGzipped = true) .getOrElseF(NotFound()) } } object Statics { def apply[F[_]: Sync: ContextShift]: Statics[F] = new Statics[F] }
Example 28
Source File: PushEvents.scala From event-sourcing-kafka-streams with MIT License | 5 votes |
package org.amitayh.invoices.web import java.util.UUID import cats.effect._ import fs2.concurrent.Topic import io.circe.generic.auto._ import io.circe.syntax._ import org.amitayh.invoices.common.domain.{CommandResult, InvoiceSnapshot} import org.amitayh.invoices.dao.InvoiceRecord import org.amitayh.invoices.web.PushEvents._ import org.http4s.dsl.Http4sDsl import org.http4s.{HttpRoutes, ServerSentEvent} class PushEvents[F[_]: Concurrent] extends Http4sDsl[F] { private val maxQueued = 16 def service(commandResultsTopic: Topic[F, CommandResultRecord], invoiceUpdatesTopic: Topic[F, InvoiceSnapshotRecord]): HttpRoutes[F] = HttpRoutes.of[F] { case GET -> Root / UuidVar(originId) => val commandResults = commandResultsTopic.subscribe(maxQueued).collect { case Some((_, result)) if result.originId == originId => Event(result).asServerSentEvent } val invoiceUpdates = invoiceUpdatesTopic.subscribe(maxQueued).collect { case Some((id, snapshot)) => Event(id, snapshot).asServerSentEvent } Ok(commandResults merge invoiceUpdates) } } object PushEvents { type CommandResultRecord = Option[(UUID, CommandResult)] type InvoiceSnapshotRecord = Option[(UUID, InvoiceSnapshot)] def apply[F[_]: Concurrent]: PushEvents[F] = new PushEvents[F] } sealed trait Event { def asServerSentEvent: ServerSentEvent = ServerSentEvent(this.asJson.noSpaces) } case class CommandSucceeded(commandId: UUID) extends Event case class CommandFailed(commandId: UUID, cause: String) extends Event case class InvoiceUpdated(record: InvoiceRecord) extends Event object Event { def apply(result: CommandResult): Event = result match { case CommandResult(_, commandId, _: CommandResult.Success) => CommandSucceeded(commandId) case CommandResult(_, commandId, CommandResult.Failure(cause)) => CommandFailed(commandId, cause.message) } def apply(id: UUID, snapshot: InvoiceSnapshot): Event = InvoiceUpdated(InvoiceRecord(id, snapshot)) }
Example 29
Source File: Http4sRoutingModule.scala From scala-server-toolkit with MIT License | 5 votes |
package com.avast.sst.example.module import cats.implicits._ import com.avast.sst.example.service.RandomService import com.avast.sst.http4s.server.Http4sRouting import com.avast.sst.http4s.server.micrometer.MicrometerHttp4sServerMetricsModule import org.http4s.client.Client import org.http4s.dsl.Http4sDsl import org.http4s.{HttpApp, HttpRoutes} import zio.Task import zio.interop.catz._ class Http4sRoutingModule( randomService: RandomService, client: Client[Task], serverMetricsModule: MicrometerHttp4sServerMetricsModule[Task] ) extends Http4sDsl[Task] { import serverMetricsModule._ private val helloWorldRoute = routeMetrics.wrap("hello")(Ok("Hello World!")) private val routes = HttpRoutes.of[Task] { case GET -> Root / "hello" => helloWorldRoute case GET -> Root / "random" => randomService.randomNumber.map(_.show).flatMap(Ok(_)) case GET -> Root / "circuit-breaker" => client.expect[String]("https://httpbin.org/status/500").flatMap(Ok(_)) } val router: HttpApp[Task] = Http4sRouting.make { serverMetrics { routes } } }
Example 30
Source File: Http4sBlazeServerModuleTest.scala From scala-server-toolkit with MIT License | 5 votes |
package com.avast.sst.http4s.server import cats.effect.{ContextShift, IO, Timer} import com.avast.sst.http4s.client.{Http4sBlazeClientConfig, Http4sBlazeClientModule} import org.http4s.HttpRoutes import org.http4s.dsl.Http4sDsl import org.scalatest.funsuite.AsyncFunSuite import scala.concurrent.ExecutionContext class Http4sBlazeServerModuleTest extends AsyncFunSuite with Http4sDsl[IO] { implicit private val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global) implicit private val timer: Timer[IO] = IO.timer(ExecutionContext.global) test("Simple HTTP server") { val routes = Http4sRouting.make(HttpRoutes.of[IO] { case GET -> Root / "test" => Ok("test") }) val test = for { server <- Http4sBlazeServerModule.make[IO](Http4sBlazeServerConfig("127.0.0.1", 0), routes, ExecutionContext.global) client <- Http4sBlazeClientModule.make[IO](Http4sBlazeClientConfig(), ExecutionContext.global) } yield (server, client) test .use { case (server, client) => client .expect[String](s"http://${server.address.getHostString}:${server.address.getPort}/test") .map(response => assert(response === "test")) } .unsafeToFuture() } }
Example 31
Source File: CorrelationIdMiddleware.scala From scala-server-toolkit with MIT License | 5 votes |
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 32
Source File: CorrelationIdMiddlewareTest.scala From scala-server-toolkit with MIT License | 5 votes |
package com.avast.sst.http4s.server.middleware import java.net.InetSocketAddress import cats.effect.{ContextShift, IO, Resource, Timer} import com.avast.sst.http4s.server.Http4sRouting import org.http4s.client.blaze.BlazeClientBuilder import org.http4s.dsl.Http4sDsl import org.http4s.server.blaze.BlazeServerBuilder import org.http4s.util.CaseInsensitiveString import org.http4s.{Header, HttpRoutes, Request, Uri} import org.scalatest.funsuite.AsyncFunSuite import scala.concurrent.ExecutionContext @SuppressWarnings(Array("scalafix:Disable.get", "scalafix:Disable.toString", "scalafix:Disable.createUnresolved")) class CorrelationIdMiddlewareTest extends AsyncFunSuite with Http4sDsl[IO] { implicit private val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global) implicit private val timer: Timer[IO] = IO.timer(ExecutionContext.global) test("CorrelationIdMiddleware fills Request attributes and HTTP response header") { val test = for { middleware <- Resource.liftF(CorrelationIdMiddleware.default[IO]) routes = Http4sRouting.make { middleware.wrap { HttpRoutes.of[IO] { case req @ GET -> Root / "test" => val id = middleware.retrieveCorrelationId(req) Ok("test").map(_.withHeaders(Header("Attribute-Value", id.toString))) } } } server <- BlazeServerBuilder[IO](ExecutionContext.global) .bindSocketAddress(InetSocketAddress.createUnresolved("127.0.0.1", 0)) .withHttpApp(routes) .resource client <- BlazeClientBuilder[IO](ExecutionContext.global).resource } yield (server, client) test .use { case (server, client) => client .run( Request[IO](uri = Uri.unsafeFromString(s"http://${server.address.getHostString}:${server.address.getPort}/test")) .withHeaders(Header("Correlation-Id", "test-value")) ) .use { response => IO.delay { assert(response.headers.get(CaseInsensitiveString("Correlation-Id")).get.value === "test-value") assert(response.headers.get(CaseInsensitiveString("Attribute-Value")).get.value === "Some(CorrelationId(test-value))") } } } .unsafeToFuture() } }
Example 33
Source File: HttpErrorHandler.scala From codepropertygraph with Apache License 2.0 | 5 votes |
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 34
Source File: MetricsMiddleware.scala From iotchain with MIT License | 5 votes |
package jbok.network.http.server.middleware import cats.effect.{Clock, Effect, Sync} import cats.implicits._ import jbok.common.metrics.PrometheusMetrics import org.http4s.HttpRoutes import org.http4s.metrics.prometheus.{Prometheus, PrometheusExportService} import org.http4s.server.middleware object MetricsMiddleware { def exportService[F[_]](implicit F: Sync[F]): F[HttpRoutes[F]] = for { _ <- PrometheusExportService.addDefaults[F](PrometheusMetrics.registry) } yield PrometheusExportService.service[F](PrometheusMetrics.registry) def apply[F[_]](routes: HttpRoutes[F], enableMetrics: Boolean)(implicit F: Effect[F], clock: Clock[F]): F[HttpRoutes[F]] = if (enableMetrics) { Prometheus[F](PrometheusMetrics.registry, "iotchain_http_server").map { metricsOps => middleware.Metrics[F](metricsOps)(routes) } } else { F.pure(routes) } }
Example 35
Source File: HmacAuthMiddleware.scala From iotchain with MIT License | 5 votes |
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 36
Source File: HmacAuthMiddlewareSpec.scala From iotchain with MIT License | 5 votes |
package jbok.network.http.server.middleware import java.time.Instant import cats.Id import cats.effect.IO import cats.implicits._ import jbok.common.CommonSpec import jbok.network.http.server.authentication.HMAC import org.http4s.dsl.io._ import org.http4s.headers.Authorization import org.http4s.implicits._ import org.http4s.{AuthScheme, Credentials, Header, HttpRoutes, Request, Status, Uri} import scodec.bits.ByteVector import tsec.mac.jca.{HMACSHA256, MacSigningKey} import scala.concurrent.duration._ class HmacAuthMiddlewareSpec extends CommonSpec { "HmacAuthMiddleware" should { val key = HMACSHA256.buildKey[Id]( ByteVector.fromValidHex("70ea14ac30939a972b5a67cab952d6d7d474727b05fe7f9283abc1e505919e83").toArray ) def sign(url: String): (String, String) = { val datetime = Instant.now().toString val signature = HMAC.http.signForHeader("GET", url, datetime, key).unsafeRunSync() (signature, datetime) } val routes = HttpRoutes.of[IO] { case GET -> Root / "ping" => Ok("pong") } val service = routes.orNotFound val req = Request[IO](uri = Uri.uri("/ping")) service.run(req).unsafeRunSync().status shouldBe Status.Ok val authedService = HmacAuthMiddleware(key)(routes).orNotFound "403 if no Authorization header" in { val resp = authedService.run(req).unsafeRunSync() val text = resp.bodyAsText.compile.foldMonoid.unsafeRunSync() resp.status shouldBe Status.Forbidden text shouldBe HmacAuthError.NoAuthHeader.message } "403 if no X-Datetime header" in { val signature = HMAC.http.signForHeader("GET", "/ping", Instant.now().toString, key).unsafeRunSync() val req = Request[IO](uri = Uri.uri("/ping")).putHeaders(Authorization(Credentials.Token(AuthScheme.Bearer, signature))) val resp = authedService.run(req).unsafeRunSync() val text = resp.bodyAsText.compile.foldMonoid.unsafeRunSync() resp.status shouldBe Status.Forbidden text shouldBe HmacAuthError.NoDatetimeHeader.message } "403 if time window is closed" in { val authedService = HmacAuthMiddleware(key, 2.seconds)(routes).orNotFound val now = Instant.now() val signature = HMAC.http.signForHeader("GET", "/ping", now.toString, key).unsafeRunSync() val req = Request[IO](uri = Uri.uri("/ping")) .putHeaders( Authorization(Credentials.Token(AuthScheme.Bearer, signature)), Header("X-Datetime", now.toString) ) val resp = authedService.run(req).unsafeRunSync() resp.status shouldBe Status.Ok IO.sleep(3.seconds).unsafeRunSync() val resp2 = authedService.run(req).unsafeRunSync() val text = resp2.bodyAsText.compile.foldMonoid.unsafeRunSync() resp2.status shouldBe Status.Forbidden text shouldBe HmacAuthError.Timeout.message } "helper" in { val (sig, date) = sign("/v1/blocks") println(("Authorization", s"Bearer $sig")) println(("X-Datetime", date)) println(("Random key", ByteVector(MacSigningKey.toJavaKey[HMACSHA256](HMACSHA256.generateKey[Id]).getEncoded).toHex)) } } }
Example 37
Source File: HttpService.scala From iotchain with MIT License | 5 votes |
package jbok.app.service import cats.effect.{ConcurrentEffect, Resource, Timer} import io.circe.Json import cats.implicits._ import fs2._ import javax.net.ssl.SSLContext import jbok.network.http.server.middleware.{CORSMiddleware, GzipMiddleware, LoggerMiddleware, MetricsMiddleware} import jbok.core.config.ServiceConfig import jbok.core.api._ import jbok.crypto.ssl.SSLConfig import jbok.network.rpc.RpcService import jbok.network.rpc.http.Http4sRpcServer import org.http4s.HttpRoutes import org.http4s.implicits._ import org.http4s.server.{SSLClientAuthMode, Server} import org.http4s.server.blaze.BlazeServerBuilder final class HttpService[F[_]]( config: ServiceConfig, sslConfig: SSLConfig, account: AccountAPI[F], admin: AdminAPI[F], block: BlockAPI[F], contract: ContractAPI[F], miner: MinerAPI[F], personal: PersonalAPI[F], transaction: TransactionAPI[F], sslOpt: Option[SSLContext] )(implicit F: ConcurrentEffect[F], T: Timer[F]) { import jbok.codec.impl.circe._ import _root_.io.circe.generic.auto._ import jbok.codec.json.implicits._ val rpcService: RpcService[F, Json] = { var service = RpcService[F, Json] if (config.apis.contains("account")) service = service.mount(account) else () if (config.apis.contains("admin")) service = service.mount(admin) else () if (config.apis.contains("block")) service = service.mount(block) else () if (config.apis.contains("contract")) service = service.mount(contract) else () if (config.apis.contains("miner")) service = service.mount(miner) else () if (config.apis.contains("personal")) service = service.mount(personal) else () if (config.apis.contains("transaction")) service = service.mount(transaction) else () service } val routes: HttpRoutes[F] = Http4sRpcServer.routes(rpcService) private val builder: F[BlazeServerBuilder[F]] = { val httpApp = for { exportRoute <- MetricsMiddleware.exportService[F] withMetrics <- MetricsMiddleware[F](routes, config.enableMetrics) withLogger = LoggerMiddleware[F](config.logHeaders, config.logBody)((withMetrics <+> exportRoute).orNotFound) withCORS = CORSMiddleware[F](withLogger, config.allowedOrigins) app = GzipMiddleware[F](withCORS) } yield app val builder = httpApp.map { app => BlazeServerBuilder[F] .withHttpApp(app) .withNio2(true) .enableHttp2(config.enableHttp2) .withWebSockets(config.enableWebsockets) .bindHttp(config.port, config.local) } val sslLClientAuthMode = sslConfig.clientAuth match { case "NotRequested" => SSLClientAuthMode.NotRequested case "Requested" => SSLClientAuthMode.Requested case "Required" => SSLClientAuthMode.Requested case x => throw new IllegalArgumentException(s"SSLClientAuthMode ${x} is not supported") } sslOpt match { case Some(ssl) => builder.map(_.withSSLContext(ssl, sslLClientAuthMode)) case None => builder.map(_.enableHttp2(false)) } } val resource: Resource[F, Server[F]] = Resource.liftF(builder).flatMap(_.resource) val stream: Stream[F, Unit] = if (config.enable) { Stream.eval(builder).flatMap(_.serve).drain } else { Stream.empty } }
Example 38
Source File: Server.scala From zio-metrics with Apache License 2.0 | 5 votes |
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 39
Source File: http4s.scala From sup with Apache License 2.0 | 5 votes |
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 40
Source File: jwtStatefulExample.scala From tsec with MIT License | 5 votes |
package http4sExamples import cats.Id import cats.effect.IO import cats.syntax.semigroupk._ import org.http4s.HttpRoutes import org.http4s.dsl.io._ import tsec.authentication._ import tsec.common.SecureRandomId import tsec.mac.jca.{HMACSHA256, MacSigningKey} import scala.concurrent.duration._ object jwtStatefulExample { import ExampleAuthHelpers._ type AuthService = TSecAuthService[User, AugmentedJWT[HMACSHA256, Int], IO] val jwtStore = dummyBackingStore[IO, SecureRandomId, AugmentedJWT[HMACSHA256, Int]](s => SecureRandomId.coerce(s.id)) //We create a way to store our users. You can attach this to say, your doobie accessor val userStore: BackingStore[IO, Int, User] = dummyBackingStore[IO, Int, User](_.id) val signingKey : MacSigningKey[HMACSHA256] = HMACSHA256.generateKey[Id] //Our signing key. Instantiate in a safe way using GenerateLift val jwtStatefulAuth = JWTAuthenticator.backed.inBearerToken( expiryDuration = 10.minutes, //Absolute expiration time maxIdle = None, tokenStore = jwtStore, identityStore = userStore, signingKey = signingKey ) val Auth = SecuredRequestHandler(jwtStatefulAuth) val r: SecuredRequest[IO, User, AugmentedJWT[HMACSHA256, Int]] = request Ok() } val service2: AuthService = TSecAuthService { case request @ GET -> Root / "api2" asAuthed user => val r: SecuredRequest[IO, User, AugmentedJWT[HMACSHA256, Int]] = request Ok() } val liftedService1: HttpRoutes[IO] = Auth.liftService(service1) val liftedComposed: HttpRoutes[IO] = Auth.liftService(service1 <+> service2) }
Example 41
Source File: EncryptedCookieExample.scala From tsec with MIT License | 5 votes |
package http4sExamples import java.util.UUID import cats.effect.IO import cats.syntax.semigroupk._ import org.http4s.HttpRoutes import org.http4s.dsl.io._ import tsec.authentication._ import tsec.cipher.symmetric.jca._ import scala.concurrent.duration._ object EncryptedCookieExample { import ExampleAuthHelpers._ type AuthService = TSecAuthService[User, AuthEncryptedCookie[AES128GCM, Int], IO] implicit val encryptor = AES128GCM.genEncryptor[IO] implicit val gcmstrategy = AES128GCM.defaultIvStrategy[IO] val cookieBackingStore: BackingStore[IO, UUID, AuthEncryptedCookie[AES128GCM, Int]] = dummyBackingStore[IO, UUID, AuthEncryptedCookie[AES128GCM, Int]](_.id) // We create a way to store our users. You can attach this to say, your doobie accessor val userStore: BackingStore[IO, Int, User] = dummyBackingStore[IO, Int, User](_.id) val settings: TSecCookieSettings = TSecCookieSettings( cookieName = "tsec-auth", secure = false, expiryDuration = 10.minutes, // Absolute expiration time maxIdle = None // Rolling window expiration. Set this to a FiniteDuration if you intend to have one ) val key: SecretKey[AES128GCM] = AES128GCM.unsafeGenerateKey //Our encryption key val authWithBackingStore = //Instantiate a stateful authenticator EncryptedCookieAuthenticator.withBackingStore( settings, cookieBackingStore, userStore, key ) val stateless = //Instantiate a stateless authenticator EncryptedCookieAuthenticator.stateless( settings, userStore, key ) val Auth = SecuredRequestHandler(stateless) val r: SecuredRequest[IO, User, AuthEncryptedCookie[AES128GCM, Int]] = request Ok() } val rawService2: AuthService = TSecAuthService { case request @ GET -> Root / "api2" asAuthed user => val r: SecuredRequest[IO, User, AuthEncryptedCookie[AES128GCM, Int]] = request Ok() } val liftedService: HttpRoutes[IO] = Auth.liftService(rawService1) val liftedComposed: HttpRoutes[IO] = Auth.liftService(rawService1 <+> rawService2) }
Example 42
Source File: BearerTokenExample.scala From tsec with MIT License | 5 votes |
package http4sExamples import cats.effect.IO import cats.syntax.semigroupk._ import org.http4s.HttpRoutes import org.http4s.dsl.io._ import tsec.authentication._ import tsec.common.SecureRandomId import scala.concurrent.duration._ object BearerTokenExample { import ExampleAuthHelpers._ val bearerTokenStore = dummyBackingStore[IO, SecureRandomId, TSecBearerToken[Int]](s => SecureRandomId.coerce(s.id)) type AuthService = TSecAuthService[User, TSecBearerToken[Int], IO] //We create a way to store our users. You can attach this to say, your doobie accessor val userStore: BackingStore[IO, Int, User] = dummyBackingStore[IO, Int, User](_.id) val settings: TSecTokenSettings = TSecTokenSettings( expiryDuration = 10.minutes, //Absolute expiration time maxIdle = None ) val bearerTokenAuth = BearerTokenAuthenticator( bearerTokenStore, userStore, settings ) val Auth = SecuredRequestHandler(bearerTokenAuth) val authService1: AuthService = TSecAuthService { //Where user is the case class User above case request @ GET -> Root / "api" asAuthed user => val r: SecuredRequest[IO, User, TSecBearerToken[Int]] = request Ok() } val authedService2: AuthService = TSecAuthService { case GET -> Root / "api2" asAuthed user => Ok() } val lifted: HttpRoutes[IO] = Auth.liftService(authService1) val liftedComposed: HttpRoutes[IO] = Auth.liftService(authService1 <+> authedService2) }
Example 43
Source File: SignedCookieExample.scala From tsec with MIT License | 5 votes |
package http4sExamples import java.util.UUID import cats.Id import cats.effect.IO import cats.syntax.semigroupk._ import org.http4s.HttpRoutes import org.http4s.dsl.io._ import tsec.authentication._ import tsec.mac.jca.{HMACSHA256, MacSigningKey} import scala.concurrent.duration._ object SignedCookieExample { import ExampleAuthHelpers._ type AuthService = TSecAuthService[User, AuthenticatedCookie[HMACSHA256, Int], IO] val cookieBackingStore: BackingStore[IO, UUID, AuthenticatedCookie[HMACSHA256, Int]] = dummyBackingStore[IO, UUID, AuthenticatedCookie[HMACSHA256, Int]](_.id) // We create a way to store our users. You can attach this to say, your doobie accessor val userStore: BackingStore[IO, Int, User] = dummyBackingStore[IO, Int, User](_.id) val settings: TSecCookieSettings = TSecCookieSettings( cookieName = "tsec-auth", secure = false, expiryDuration = 10.minutes, // Absolute expiration time maxIdle = None // Rolling window expiration. Set this to a FiniteDuration if you intend to have one ) //Our Signing key. Instantiate in a safe way using generateKey[F] where F[_]: Sync val key: MacSigningKey[HMACSHA256] = HMACSHA256.generateKey[Id] val cookieAuth = SignedCookieAuthenticator( settings, cookieBackingStore, userStore, key ) val Auth = SecuredRequestHandler(cookieAuth) val service1: AuthService = TSecAuthService { //Where user is the case class User above case request @ GET -> Root / "api" asAuthed user => val r: SecuredRequest[IO, User, AuthenticatedCookie[HMACSHA256, Int]] = request Ok() } val service2: AuthService = TSecAuthService { case request @ GET -> Root / "api2" asAuthed user => val r: SecuredRequest[IO, User, AuthenticatedCookie[HMACSHA256, Int]] = request Ok() } val liftedService1: HttpRoutes[IO] = Auth.liftService(service1) val liftedComposed: HttpRoutes[IO] = Auth.liftService(service1 <+> service2) }
Example 44
Source File: KamonSupport.scala From kamon-http4s with Apache License 2.0 | 5 votes |
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 45
Source File: HttpMetricsSpec.scala From kamon-http4s with Apache License 2.0 | 5 votes |
package kamon.http4s import cats.effect._ import kamon.testkit.InstrumentInspection import org.http4s.HttpRoutes import org.http4s.dsl.io._ import org.http4s.server.Server import org.http4s.server.blaze.BlazeServerBuilder import org.scalatest.concurrent.Eventually import org.scalatest.time.SpanSugar import org.scalatest.{Matchers, OptionValues, WordSpec} import cats.implicits._ import kamon.http4s.middleware.server.KamonSupport import kamon.instrumentation.http.HttpServerMetrics import org.http4s.client.blaze.BlazeClientBuilder import org.http4s.client.Client import scala.concurrent.ExecutionContext import org.http4s.implicits._ class HttpMetricsSpec extends WordSpec with Matchers with Eventually with SpanSugar with InstrumentInspection.Syntax with OptionValues { implicit val contextShift: ContextShift[IO] = IO.contextShift(ExecutionContext.global) implicit val timer: Timer[IO] = IO.timer(ExecutionContext.global) val srv = BlazeServerBuilder[IO] .bindLocal(43567) .withHttpApp(KamonSupport(HttpRoutes.of[IO] { case GET -> Root / "tracing" / "ok" => Ok("ok") case GET -> Root / "tracing" / "not-found" => NotFound("not-found") case GET -> Root / "tracing" / "error" => InternalServerError("This page will generate an error!") }, "/127.0.0.1", 43567).orNotFound) .resource val client = BlazeClientBuilder[IO](ExecutionContext.global).withMaxTotalConnections(10).resource val metrics = Resource.liftF(IO(HttpServerMetrics.of("http4s.server", "/127.0.0.1", 43567))) def withServerAndClient[A](f: (Server[IO], Client[IO], HttpServerMetrics.HttpServerInstruments) => IO[A]): A = (srv, client, metrics).tupled.use(f.tupled).unsafeRunSync() private def get[F[_]: ConcurrentEffect](path: String)(server: Server[F], client: Client[F]): F[String] = { client.expect[String](s"http://127.0.0.1:${server.address.getPort}$path") } "The HttpMetrics" should { "track the total of active requests" in withServerAndClient { (server, client, serverMetrics) => val requests = List .fill(100) { get("/tracing/ok")(server, client) }.parSequence_ val test = IO { serverMetrics.activeRequests.distribution().max should be > 1L serverMetrics.activeRequests.distribution().min shouldBe 0L } requests *> test } "track the response time with status code 2xx" in withServerAndClient { (server, client, serverMetrics) => val requests: IO[Unit] = List.fill(100)(get("/tracing/ok")(server, client)).sequence_ val test = IO(serverMetrics.requestsSuccessful.value should be >= 0L) requests *> test } "track the response time with status code 4xx" in withServerAndClient { (server, client, serverMetrics) => val requests: IO[Unit] = List.fill(100)(get("/tracing/not-found")(server, client).attempt).sequence_ val test = IO(serverMetrics.requestsClientError.value should be >= 0L) requests *> test } "track the response time with status code 5xx" in withServerAndClient { (server, client, serverMetrics) => val requests: IO[Unit] = List.fill(100)(get("/tracing/error")(server, client).attempt).sequence_ val test = IO(serverMetrics.requestsServerError.value should be >= 0L) requests *> test } } }
Example 46
Source File: ServerInterpreterTest.scala From endpoints4s with MIT License | 5 votes |
package endpoints4s.http4s.server import java.net.ServerSocket import cats.effect.{ContextShift, IO, Timer} import endpoints4s.{Invalid, Valid} import endpoints4s.algebra.server.{ BasicAuthenticationTestSuite, DecodedUrl, EndpointsTestSuite, JsonEntitiesFromSchemasTestSuite, SumTypedEntitiesTestSuite, TextEntitiesTestSuite } import org.http4s.server.Router import org.http4s.{HttpRoutes, Uri} import org.http4s.server.blaze.BlazeServerBuilder import org.http4s.syntax.kleisli._ import scala.concurrent.ExecutionContext class ServerInterpreterTest extends EndpointsTestSuite[EndpointsTestApi] with BasicAuthenticationTestSuite[EndpointsTestApi] with JsonEntitiesFromSchemasTestSuite[EndpointsTestApi] with TextEntitiesTestSuite[EndpointsTestApi] with SumTypedEntitiesTestSuite[EndpointsTestApi] { val serverApi = new EndpointsTestApi() def decodeUrl[A](url: serverApi.Url[A])(rawValue: String): DecodedUrl[A] = { val uri = Uri.fromString(rawValue).getOrElse(sys.error(s"Illegal URI: $rawValue")) url.decodeUrl(uri) match { case None => DecodedUrl.NotMatched case Some(Invalid(errors)) => DecodedUrl.Malformed(errors) case Some(Valid(a)) => DecodedUrl.Matched(a) } } private def serveGeneralEndpoint[Req, Resp]( endpoint: serverApi.Endpoint[Req, Resp], request2response: Req => Resp )(runTests: Int => Unit): Unit = { val port = { val socket = new ServerSocket(0) try socket.getLocalPort finally if (socket != null) socket.close() } implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global) implicit val timer: Timer[IO] = IO.timer(ExecutionContext.global) val service = HttpRoutes.of[IO](endpoint.implementedBy(request2response)) val httpApp = Router("/" -> service).orNotFound val server = BlazeServerBuilder[IO](ExecutionContext.global) .bindHttp(port, "localhost") .withHttpApp(httpApp) server.resource.use(_ => IO(runTests(port))).unsafeRunSync() } def serveEndpoint[Resp]( endpoint: serverApi.Endpoint[_, Resp], response: => Resp )(runTests: Int => Unit): Unit = serveGeneralEndpoint(endpoint, (_: Any) => response)(runTests) def serveIdentityEndpoint[Resp]( endpoint: serverApi.Endpoint[Resp, Resp] )(runTests: Int => Unit): Unit = serveGeneralEndpoint(endpoint, identity[Resp])(runTests) }
Example 47
Source File: Api.scala From endpoints4s with MIT License | 5 votes |
package sample import cats.effect.IO import endpoints4s.http4s.server.{BasicAuthentication, Endpoints, JsonEntitiesFromCodecs} import org.http4s.HttpRoutes import scala.util.Random object Api extends Endpoints[IO] with JsonEntitiesFromCodecs with BasicAuthentication with ApiAlg { val router: HttpRoutes[IO] = HttpRoutes.of( routesFromEndpoints( index.implementedBy { case (name, age, _) => User(name, age) }, maybe.implementedBy(_ => if (util.Random.nextBoolean()) Some(()) else None) orElse action.implementedBy { _ => ActionResult("Action") }, actionFut.implementedByEffect { _ => IO.pure(ActionResult("Action")) }, auth.implementedBy { credentials => println(s"Authenticated request: ${credentials.username}") if (Random.nextBoolean()) Some(()) else None // Randomly return a forbidden } ) ) }
Example 48
Source File: VCSExtraAlgTest.scala From scala-steward with Apache License 2.0 | 5 votes |
package org.scalasteward.core.vcs import cats.effect.IO import org.http4s.HttpRoutes import org.http4s.client.Client import org.http4s.dsl.io._ import org.http4s.implicits._ import org.scalasteward.core.TestInstances.ioLogger import org.scalasteward.core.TestSyntax._ import org.scalasteward.core.data.{ReleaseRelatedUrl, Update} import org.scalasteward.core.mock.MockContext.config import org.scalasteward.core.util.{HttpExistenceClient, Nel} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers class VCSExtraAlgTest extends AnyFunSuite with Matchers { val routes: HttpRoutes[IO] = HttpRoutes.of[IO] { case HEAD -> Root / "foo" / "bar" / "compare" / "v0.1.0...v0.2.0" => Ok("exist") case HEAD -> Root / "foo" / "buz" / "compare" / "v0.1.0...v0.2.0" => PermanentRedirect() case _ => NotFound() } implicit val client = Client.fromHttpApp[IO](routes.orNotFound) implicit val httpExistenceClient = HttpExistenceClient.create[IO].allocated.map(_._1).unsafeRunSync() val vcsExtraAlg = VCSExtraAlg.create[IO] val updateFoo = Update.Single("com.example" % "foo" % "0.1.0", Nel.of("0.2.0")) val updateBar = Update.Single("com.example" % "bar" % "0.1.0", Nel.of("0.2.0")) val updateBuz = Update.Single("com.example" % "buz" % "0.1.0", Nel.of("0.2.0")) test("getBranchCompareUrl") { vcsExtraAlg .getReleaseRelatedUrls(uri"https://github.com/foo/foo", updateFoo) .unsafeRunSync() shouldBe List.empty vcsExtraAlg .getReleaseRelatedUrls(uri"https://github.com/foo/bar", updateBar) .unsafeRunSync() shouldBe List( ReleaseRelatedUrl.VersionDiff(uri"https://github.com/foo/bar/compare/v0.1.0...v0.2.0") ) vcsExtraAlg .getReleaseRelatedUrls(uri"https://github.com/foo/buz", updateBuz) .unsafeRunSync() shouldBe List.empty } }
Example 49
Source File: Http4sRpcServer.scala From iotchain with MIT License | 5 votes |
package jbok.network.rpc.http import cats.effect.{ConcurrentEffect, Resource, Sync, Timer} import cats.implicits._ import io.circe.Json import io.circe.syntax._ import jbok.network.rpc.{RpcRequest, RpcService} import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityCodec._ import org.http4s.dsl.Http4sDsl import org.http4s.implicits._ import org.http4s.server.Server import org.http4s.server.blaze.BlazeServerBuilder object Http4sRpcServer { def routes[F[_]](service: RpcService[F, Json])(implicit F: Sync[F]): HttpRoutes[F] = { val dsl = Http4sDsl[F] import dsl._ HttpRoutes.of[F] { case req @ POST -> path => for { json <- req.as[Json] result <- service.handle(RpcRequest(path.toList, json)) resp <- Ok(result.asJson) } yield resp } } def server[F[_]](service: RpcService[F, Json])(implicit F: ConcurrentEffect[F], T: Timer[F]): Resource[F, Server[F]] = BlazeServerBuilder[F] .bindLocal(0) .withHttpApp(routes[F](service).orNotFound) .withWebSockets(true) .resource }
Example 50
Source File: InfoRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.joex.routes import cats.effect.Sync import docspell.joex.BuildInfo import docspell.joexapi.model.VersionInfo import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl object InfoRoutes { def apply[F[_]: Sync](): HttpRoutes[F] = { val dsl = new Http4sDsl[F] {} import dsl._ HttpRoutes.of[F] { case GET -> (Root / "version") => Ok( VersionInfo( BuildInfo.version, BuildInfo.builtAtMillis, BuildInfo.builtAtString, BuildInfo.gitHeadCommit.getOrElse(""), BuildInfo.gitDescribedVersion.getOrElse("") ) ) } } }
Example 51
Source File: JoexRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.joex.routes import cats.effect._ import cats.implicits._ import docspell.common.{Duration, Ident, Timestamp} import docspell.joex.JoexApp import docspell.joexapi.model._ import docspell.store.records.{RJob, RJobLog} import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl object JoexRoutes { def apply[F[_]: ConcurrentEffect: Timer](app: JoexApp[F]): HttpRoutes[F] = { val dsl = new Http4sDsl[F] {} import dsl._ HttpRoutes.of[F] { case POST -> Root / "notify" => for { _ <- app.scheduler.notifyChange _ <- app.periodicScheduler.notifyChange resp <- Ok(BasicResult(true, "Schedulers notified.")) } yield resp case GET -> Root / "running" => for { jobs <- app.scheduler.getRunning jj = jobs.map(mkJob) resp <- Ok(JobList(jj.toList)) } yield resp case POST -> Root / "shutdownAndExit" => for { _ <- ConcurrentEffect[F].start( Timer[F].sleep(Duration.seconds(1).toScala) *> app.initShutdown ) resp <- Ok(BasicResult(true, "Shutdown initiated.")) } yield resp case GET -> Root / "job" / Ident(id) => for { optJob <- app.scheduler.getRunning.map(_.find(_.id == id)) optLog <- optJob.traverse(j => app.findLogs(j.id)) jAndL = for { job <- optJob; log <- optLog } yield mkJobLog(job, log) resp <- jAndL.map(Ok(_)).getOrElse(NotFound(BasicResult(false, "Not found"))) } yield resp case POST -> Root / "job" / Ident(id) / "cancel" => for { flag <- app.scheduler.requestCancel(id) resp <- Ok( BasicResult(flag, if (flag) "Cancel request submitted" else "Job not found") ) } yield resp } } def mkJob(j: RJob): Job = Job( j.id, j.subject, j.submitted, j.priority, j.retries, j.progress, j.started.getOrElse(Timestamp.Epoch) ) def mkJobLog(j: RJob, jl: Vector[RJobLog]): JobAndLog = JobAndLog(mkJob(j), jl.map(r => JobLogEvent(r.created, r.level, r.message)).toList) }
Example 52
Source File: InfoRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.routes import cats.effect.Sync import docspell.restapi.model.VersionInfo import docspell.restserver.BuildInfo import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl object InfoRoutes { def apply[F[_]: Sync](): HttpRoutes[F] = { val dsl = new Http4sDsl[F] {} import dsl._ HttpRoutes.of[F] { case GET -> (Root / "version") => Ok( VersionInfo( BuildInfo.version, BuildInfo.builtAtMillis, BuildInfo.builtAtString, BuildInfo.gitHeadCommit.getOrElse(""), BuildInfo.gitDescribedVersion.getOrElse("") ) ) } } }
Example 53
Source File: TagRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.routes import cats.effect._ import cats.implicits._ import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.common.Ident import docspell.restapi.model._ import docspell.restserver.conv.Conversions._ import docspell.restserver.http4s._ import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl object TagRoutes { def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = { val dsl = new Http4sDsl[F] with ResponseGenerator[F] {} import dsl._ HttpRoutes.of { case GET -> Root :? QueryParam.QueryOpt(q) => for { all <- backend.tag.findAll(user.account, q.map(_.q)) resp <- Ok(TagList(all.size, all.map(mkTag).toList)) } yield resp case req @ POST -> Root => for { data <- req.as[Tag] tag <- newTag(data, user.account.collective) res <- backend.tag.add(tag) resp <- Ok(basicResult(res, "Tag successfully created.")) } yield resp case req @ PUT -> Root => for { data <- req.as[Tag] tag = changeTag(data, user.account.collective) res <- backend.tag.update(tag) resp <- Ok(basicResult(res, "Tag successfully updated.")) } yield resp case DELETE -> Root / Ident(id) => for { del <- backend.tag.delete(id, user.account.collective) resp <- Ok(basicResult(del, "Tag successfully deleted.")) } yield resp } } }
Example 54
Source File: EquipmentRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.routes import cats.effect._ import cats.implicits._ import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.common.Ident import docspell.restapi.model._ import docspell.restserver.conv.Conversions._ import docspell.restserver.http4s.QueryParam import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl object EquipmentRoutes { def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = { val dsl = new Http4sDsl[F] {} import dsl._ HttpRoutes.of { case GET -> Root :? QueryParam.QueryOpt(q) => for { data <- backend.equipment.findAll(user.account, q.map(_.q)) resp <- Ok(EquipmentList(data.map(mkEquipment).toList)) } yield resp case req @ POST -> Root => for { data <- req.as[Equipment] equip <- newEquipment(data, user.account.collective) res <- backend.equipment.add(equip) resp <- Ok(basicResult(res, "Equipment created")) } yield resp case req @ PUT -> Root => for { data <- req.as[Equipment] equip = changeEquipment(data, user.account.collective) res <- backend.equipment.update(equip) resp <- Ok(basicResult(res, "Equipment updated.")) } yield resp case DELETE -> Root / Ident(id) => for { del <- backend.equipment.delete(id, user.account.collective) resp <- Ok(basicResult(del, "Equipment deleted.")) } yield resp } } }
Example 55
Source File: UserRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.routes import cats.effect._ import cats.implicits._ import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.common.Ident import docspell.restapi.model._ import docspell.restserver.conv.Conversions._ import docspell.restserver.http4s.ResponseGenerator import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl object UserRoutes { def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = { val dsl = new Http4sDsl[F] with ResponseGenerator[F] {} import dsl._ HttpRoutes.of { case req @ POST -> Root / "changePassword" => for { data <- req.as[PasswordChange] res <- backend.collective.changePassword( user.account, data.currentPassword, data.newPassword ) resp <- Ok(basicResult(res)) } yield resp case GET -> Root => for { all <- backend.collective.listUser(user.account.collective) res <- Ok(UserList(all.map(mkUser).toList)) } yield res case req @ POST -> Root => for { data <- req.as[User] nuser <- newUser(data, user.account.collective) added <- backend.collective.add(nuser) resp <- Ok(basicResult(added, "User created.")) } yield resp case req @ PUT -> Root => for { data <- req.as[User] nuser = changeUser(data, user.account.collective) update <- backend.collective.update(nuser) resp <- Ok(basicResult(update, "User updated.")) } yield resp case DELETE -> Root / Ident(id) => for { ar <- backend.collective.deleteUser(id, user.account.collective) resp <- Ok(basicResult(ar, "User deleted.")) } yield resp } } }
Example 56
Source File: OrganizationRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.routes import cats.effect._ import cats.implicits._ import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.common.Ident import docspell.restapi.model._ import docspell.restserver.conv.Conversions._ import docspell.restserver.http4s.QueryParam import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl object OrganizationRoutes { def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = { val dsl = new Http4sDsl[F] {} import dsl._ HttpRoutes.of { case GET -> Root :? QueryParam.FullOpt(full) +& QueryParam.QueryOpt(q) => if (full.getOrElse(false)) for { data <- backend.organization.findAllOrg(user.account, q.map(_.q)) resp <- Ok(OrganizationList(data.map(mkOrg).toList)) } yield resp else for { data <- backend.organization.findAllOrgRefs(user.account, q.map(_.q)) resp <- Ok(ReferenceList(data.map(mkIdName).toList)) } yield resp case req @ POST -> Root => for { data <- req.as[Organization] newOrg <- newOrg(data, user.account.collective) added <- backend.organization.addOrg(newOrg) resp <- Ok(basicResult(added, "New organization saved.")) } yield resp case req @ PUT -> Root => for { data <- req.as[Organization] upOrg <- changeOrg(data, user.account.collective) update <- backend.organization.updateOrg(upOrg) resp <- Ok(basicResult(update, "Organization updated.")) } yield resp case DELETE -> Root / Ident(id) => for { delOrg <- backend.organization.deleteOrg(id, user.account.collective) resp <- Ok(basicResult(delOrg, "Organization deleted.")) } yield resp } } }
Example 57
Source File: SourceRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.routes import cats.effect._ import cats.implicits._ import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.common.Ident import docspell.restapi.model._ import docspell.restserver.conv.Conversions._ import docspell.restserver.http4s.ResponseGenerator import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl object SourceRoutes { def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = { val dsl = new Http4sDsl[F] with ResponseGenerator[F] {} import dsl._ HttpRoutes.of { case GET -> Root => for { all <- backend.source.findAll(user.account) res <- Ok(SourceList(all.map(mkSource).toList)) } yield res case req @ POST -> Root => for { data <- req.as[Source] src <- newSource(data, user.account.collective) added <- backend.source.add(src) resp <- Ok(basicResult(added, "Source added.")) } yield resp case req @ PUT -> Root => for { data <- req.as[Source] src = changeSource(data, user.account.collective) updated <- backend.source.update(src) resp <- Ok(basicResult(updated, "Source updated.")) } yield resp case DELETE -> Root / Ident(id) => for { del <- backend.source.delete(id, user.account.collective) resp <- Ok(basicResult(del, "Source deleted.")) } yield resp } } }
Example 58
Source File: PersonRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.routes import cats.effect._ import cats.implicits._ import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.common.Ident import docspell.common.syntax.all._ import docspell.restapi.model._ import docspell.restserver.conv.Conversions._ import docspell.restserver.http4s.QueryParam import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl import org.log4s._ object PersonRoutes { private[this] val logger = getLogger def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = { val dsl = new Http4sDsl[F] {} import dsl._ HttpRoutes.of { case GET -> Root :? QueryParam.FullOpt(full) +& QueryParam.QueryOpt(q) => if (full.getOrElse(false)) for { data <- backend.organization.findAllPerson(user.account, q.map(_.q)) resp <- Ok(PersonList(data.map(mkPerson).toList)) } yield resp else for { data <- backend.organization.findAllPersonRefs(user.account, q.map(_.q)) resp <- Ok(ReferenceList(data.map(mkIdName).toList)) } yield resp case req @ POST -> Root => for { data <- req.as[Person] newPer <- newPerson(data, user.account.collective) added <- backend.organization.addPerson(newPer) resp <- Ok(basicResult(added, "New person saved.")) } yield resp case req @ PUT -> Root => for { data <- req.as[Person] upPer <- changePerson(data, user.account.collective) update <- backend.organization.updatePerson(upPer) resp <- Ok(basicResult(update, "Person updated.")) } yield resp case DELETE -> Root / Ident(id) => for { _ <- logger.fdebug(s"Deleting person ${id.id}") delOrg <- backend.organization.deletePerson(id, user.account.collective) resp <- Ok(basicResult(delOrg, "Person deleted.")) } yield resp } } }
Example 59
Source File: CheckFileRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.routes import cats.effect._ import cats.implicits._ import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.common.Ident import docspell.restapi.model.{BasicItem, CheckFileResult} import docspell.restserver.http4s.ResponseGenerator import docspell.store.records.RItem import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl object CheckFileRoutes { def secured[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = { val dsl = new Http4sDsl[F] with ResponseGenerator[F] {} import dsl._ HttpRoutes.of { case GET -> Root / checksum => for { items <- backend.itemSearch.findByFileCollective(checksum, user.account.collective) resp <- Ok(convert(items)) } yield resp } } def open[F[_]: Effect](backend: BackendApp[F]): HttpRoutes[F] = { val dsl = new Http4sDsl[F] with ResponseGenerator[F] {} import dsl._ HttpRoutes.of { case GET -> Root / Ident(id) / checksum => for { items <- backend.itemSearch.findByFileSource(checksum, id) resp <- Ok(convert(items)) } yield resp } } def convert(v: Vector[RItem]): CheckFileResult = CheckFileResult( v.nonEmpty, v.map(r => BasicItem(r.id, r.name, r.direction, r.state, r.created, r.itemDate)) .toList ) }
Example 60
Source File: JobQueueRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.routes import cats.effect._ import cats.implicits._ import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.common.Ident import docspell.restserver.conv.Conversions import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl object JobQueueRoutes { def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = { val dsl = new Http4sDsl[F] {} import dsl._ HttpRoutes.of { case GET -> Root / "state" => for { js <- backend.job.queueState(user.account.collective, 200) res = Conversions.mkJobQueueState(js) resp <- Ok(res) } yield resp case POST -> Root / Ident(id) / "cancel" => for { result <- backend.job.cancelJob(id, user.account.collective) resp <- Ok(Conversions.basicResult(result)) } yield resp } } }
Example 61
Source File: RegisterRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.routes import cats.effect._ import cats.implicits._ import docspell.backend.BackendApp import docspell.backend.ops.OCollective.RegisterData import docspell.backend.signup.{NewInviteResult, SignupResult} import docspell.restapi.model._ import docspell.restserver.Config import docspell.restserver.http4s.ResponseGenerator import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl import org.log4s._ object RegisterRoutes { private[this] val logger = getLogger def apply[F[_]: Effect](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = { val dsl = new Http4sDsl[F] with ResponseGenerator[F] {} import dsl._ HttpRoutes.of { case req @ POST -> Root / "register" => for { data <- req.as[Registration] res <- backend.signup.register(cfg.backend.signup)(convert(data)) resp <- Ok(convert(res)) } yield resp case req @ POST -> Root / "newinvite" => for { data <- req.as[GenInvite] res <- backend.signup.newInvite(cfg.backend.signup)(data.password) resp <- Ok(convert(res)) } yield resp } } def convert(r: NewInviteResult): InviteResult = r match { case NewInviteResult.Success(id) => InviteResult(true, "New invitation created.", Some(id)) case NewInviteResult.InvitationDisabled => InviteResult(false, "Signing up is not enabled for invitations.", None) case NewInviteResult.PasswordMismatch => InviteResult(false, "Password is invalid.", None) } def convert(r: SignupResult): BasicResult = r match { case SignupResult.CollectiveExists => BasicResult(false, "A collective with this name already exists.") case SignupResult.InvalidInvitationKey => BasicResult(false, "Invalid invitation key.") case SignupResult.SignupClosed => BasicResult(false, "Sorry, registration is closed.") case SignupResult.Failure(ex) => logger.error(ex)("Error signing up") BasicResult(false, s"Internal error: ${ex.getMessage}") case SignupResult.Success => BasicResult(true, "Signup successful") } def convert(r: Registration): RegisterData = RegisterData(r.collectiveName, r.login, r.password, r.invite) }
Example 62
Source File: CollectiveRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.routes import cats.effect._ import cats.implicits._ import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.backend.ops.OCollective import docspell.restapi.model._ import docspell.restserver.conv.Conversions import docspell.restserver.http4s._ import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl object CollectiveRoutes { def apply[F[_]: Effect](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = { val dsl = new Http4sDsl[F] with ResponseGenerator[F] {} import dsl._ HttpRoutes.of { case GET -> Root / "insights" => for { ins <- backend.collective.insights(user.account.collective) resp <- Ok(Conversions.mkItemInsights(ins)) } yield resp case req @ POST -> Root / "settings" => for { settings <- req.as[CollectiveSettings] sett = OCollective.Settings(settings.language, settings.integrationEnabled) res <- backend.collective .updateSettings(user.account.collective, sett) resp <- Ok(Conversions.basicResult(res, "Settings updated.")) } yield resp case GET -> Root / "settings" => for { collDb <- backend.collective.find(user.account.collective) sett = collDb.map(c => CollectiveSettings(c.language, c.integrationEnabled)) resp <- sett.toResponse() } yield resp case GET -> Root / "contacts" :? QueryParam.QueryOpt(q) +& QueryParam .ContactKindOpt(kind) => for { res <- backend.collective .getContacts(user.account.collective, q.map(_.q), kind) .take(50) .compile .toList resp <- Ok(ContactList(res.map(Conversions.mkContact))) } yield resp case GET -> Root => for { collDb <- backend.collective.find(user.account.collective) coll = collDb.map(c => Collective(c.id, c.state, c.created)) resp <- coll.toResponse() } yield resp } } }
Example 63
Source File: WebjarRoutes.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.restserver.webapp import cats.effect._ import org.http4s.HttpRoutes import org.http4s.server.staticcontent.NoopCacheStrategy import org.http4s.server.staticcontent.WebjarService.{Config => WebjarConfig, WebjarAsset} import org.http4s.server.staticcontent.webjarService object WebjarRoutes { def appRoutes[F[_]: Effect]( blocker: Blocker )(implicit C: ContextShift[F]): HttpRoutes[F] = webjarService( WebjarConfig( filter = assetFilter, blocker = blocker, cacheStrategy = NoopCacheStrategy[F] ) ) def assetFilter(asset: WebjarAsset): Boolean = List( ".js", ".css", ".html", ".json", ".jpg", ".png", ".eot", ".woff", ".woff2", ".svg", ".otf", ".ttf", ".yml", ".xml" ).exists(e => asset.asset.endsWith(e)) }
Example 64
Source File: TracedHttpRoute.scala From http4s-tracer with Apache License 2.0 | 5 votes |
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 } } } }