org.http4s.Request Scala Examples
The following examples show how to use org.http4s.Request.
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: 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 2
Source File: ProjectClient.scala From nexus with Apache License 2.0 | 5 votes |
package ch.epfl.bluebrain.nexus.cli.clients import cats.effect.concurrent.Ref import cats.effect.{Sync, Timer} import cats.implicits._ import ch.epfl.bluebrain.nexus.cli.config.EnvConfig import ch.epfl.bluebrain.nexus.cli.sse.{OrgLabel, OrgUuid, ProjectLabel, ProjectUuid} import ch.epfl.bluebrain.nexus.cli.{ClientErrOr, Console} import io.circe.Decoder import io.circe.generic.semiauto.deriveDecoder import org.http4s.client.Client import org.http4s.{Headers, Request} trait ProjectClient[F[_]] { final def apply[F[_]: Sync: Timer]( client: Client[F], env: EnvConfig, cache: Ref[F, Map[(OrgUuid, ProjectUuid), (OrgLabel, ProjectLabel)]], console: Console[F] ): ProjectClient[F] = { implicit val c: Console[F] = console new LiveProjectClient[F](client, env, cache) } private class LiveProjectClient[F[_]: Timer: Console: Sync]( client: Client[F], env: EnvConfig, cache: Ref[F, Map[(OrgUuid, ProjectUuid), (OrgLabel, ProjectLabel)]] ) extends AbstractHttpClient[F](client, env) with ProjectClient[F] { override def labels(org: OrgUuid, proj: ProjectUuid): F[ClientErrOr[(OrgLabel, ProjectLabel)]] = cache.get.flatMap { map => map.get((org, proj)) match { // value in cache, return case Some(value) => F.pure(Right(value)) // value not in cache, fetch, update and return case None => get(org, proj).flatMap { // propagate error case l @ Left(_) => F.pure(l) // success, update cache and return case r @ Right(value) => cache.modify(m => (m.updated((org, proj), value), value)) *> F.pure(r) } } } private def get(org: OrgUuid, proj: ProjectUuid): F[ClientErrOr[(OrgLabel, ProjectLabel)]] = { val uri = env.project(org, proj) val req = Request[F](uri = uri, headers = Headers(env.authorizationHeader.toList)) executeParse[NexusAPIProject](req).map { case Right(NexusAPIProject(orgLabel, projectLabel)) => Right((orgLabel, projectLabel)) case Left(err) => Left(err) } } } final private[ProjectClient] case class NexusAPIProject(`_organizationLabel`: OrgLabel, `_label`: ProjectLabel) private[ProjectClient] object NexusAPIProject { implicit val nexusAPIProjectDecoder: Decoder[NexusAPIProject] = deriveDecoder[NexusAPIProject] } }
Example 3
Source File: InfluxClient.scala From nexus with Apache License 2.0 | 5 votes |
package ch.epfl.bluebrain.nexus.cli.clients import cats.effect.{Sync, Timer} import cats.implicits._ import ch.epfl.bluebrain.nexus.cli._ import ch.epfl.bluebrain.nexus.cli.config.influx.InfluxConfig import ch.epfl.bluebrain.nexus.cli.config.{AppConfig, EnvConfig} import io.circe.Json import org.http4s.client.Client import org.http4s.{Method, Request, UrlForm} trait InfluxClient[F[_]] { final def apply[F[_]: Sync: Timer]( client: Client[F], config: AppConfig, console: Console[F] ): InfluxClient[F] = { implicit val c: Console[F] = console new LiveInfluxDbClient[F](client, config.influx, config.env) } }
Example 4
Source File: Http4sExtras.scala From nexus with Apache License 2.0 | 5 votes |
package ch.epfl.bluebrain.nexus.cli.utils import cats.implicits._ import ch.epfl.bluebrain.nexus.cli.sse._ import org.http4s.Credentials.Token import org.http4s.Request import org.http4s.ServerSentEvent.EventId import org.http4s.dsl.impl.{/, Root} import org.http4s.headers.{`Content-Type`, `Last-Event-Id`, Authorization} import org.http4s.util.CaseInsensitiveString import scala.util.{Success, Try} trait Http4sExtras { protected class Var[A](cast: String => Try[A]) { def unapply(str: String): Option[A] = if (!str.isEmpty) cast(str).toOption else None } object OrgUuidVar extends Var(str => Try(java.util.UUID.fromString(str)).map(OrgUuid.apply)) object ProjectUuidVar extends Var(str => Try(java.util.UUID.fromString(str)).map(ProjectUuid.apply)) object OrgLabelVar extends Var(str => Success(OrgLabel(str))) object ProjectLabelVar extends Var(str => Success(ProjectLabel(str))) object optbearer { def unapply[F[_]](request: Request[F]): Option[(Request[F], Option[BearerToken])] = request.headers.get(Authorization) match { case Some(Authorization(Token(authScheme, token))) if authScheme === CaseInsensitiveString("bearer") => Some((request, Some(BearerToken(token)))) case _ => Some((request, None)) } } object bearer { def unapply[F[_]](request: Request[F]): Option[(Request[F], BearerToken)] = optbearer.unapply(request) match { case Some((_, Some(token))) => Some((request, token)) case _ => None } } object db { def unapply[F[_]](request: Request[F]): Option[(Request[F], String)] = request.uri.params.get("db").map(request -> _) } object contentType { def unapply[F[_]](request: Request[F]): Option[(Request[F], `Content-Type`)] = request.headers.get(`Content-Type`) match { case Some(ct: `Content-Type`) => Some((request, ct)) case _ => None } } object optLastEventId { def unapply[F[_]](request: Request[F]): Option[(Request[F], Option[Offset])] = request.headers.get(`Last-Event-Id`) match { case Some(`Last-Event-Id`(EventId(value))) => Some((request, Offset(value))) case _ => Some((request, None)) } } object lastEventId { def unapply[F[_]](request: Request[F]): Option[(Request[F], Offset)] = optLastEventId.unapply(request) match { case Some((_, Some(offset))) => Some((request, offset)) case _ => None } } val v1: / = Root / "v1" } object Http4sExtras extends Http4sExtras
Example 5
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 6
Source File: LoginTest.scala From scala-pet-store with Apache License 2.0 | 5 votes |
package io.github.pauljamescleary.petstore package infrastructure.endpoint import cats.data.Kleisli import cats.effect.IO import domain.authentication.{LoginRequest, SignupRequest} import domain.users.{Role, User} import org.http4s.circe.{jsonEncoderOf, jsonOf} import org.http4s.client.dsl.Http4sClientDsl import org.http4s.{EntityDecoder, EntityEncoder, HttpApp, Request, Response} import org.http4s.implicits._ import org.http4s.headers.Authorization import io.circe.generic.auto._ import org.http4s.dsl.Http4sDsl trait LoginTest extends Http4sClientDsl[IO] with Http4sDsl[IO] { implicit val userEnc: EntityEncoder[IO, User] = jsonEncoderOf implicit val userDec: EntityDecoder[IO, User] = jsonOf implicit val signUpRequestEnc: EntityEncoder[IO, SignupRequest] = jsonEncoderOf implicit val signUpRequestDec: EntityDecoder[IO, SignupRequest] = jsonOf implicit val loginRequestEnc: EntityEncoder[IO, LoginRequest] = jsonEncoderOf implicit val loginRequestDec: EntityDecoder[IO, LoginRequest] = jsonOf def signUpAndLogIn( userSignUp: SignupRequest, userEndpoint: HttpApp[IO], ): IO[(User, Option[Authorization])] = for { signUpRq <- POST(userSignUp, uri"/users") signUpResp <- userEndpoint.run(signUpRq) user <- signUpResp.as[User] loginBody = LoginRequest(userSignUp.userName, userSignUp.password) loginRq <- POST(loginBody, uri"/users/login") loginResp <- userEndpoint.run(loginRq) } yield { user -> loginResp.headers.get(Authorization) } def signUpAndLogInAsAdmin( userSignUp: SignupRequest, userEndpoint: Kleisli[IO, Request[IO], Response[IO]], ): IO[(User, Option[Authorization])] = signUpAndLogIn(userSignUp.copy(role = Role.Admin), userEndpoint) def signUpAndLogInAsCustomer( userSignUp: SignupRequest, userEndpoint: Kleisli[IO, Request[IO], Response[IO]], ): IO[(User, Option[Authorization])] = signUpAndLogIn(userSignUp.copy(role = Role.Customer), userEndpoint) }
Example 7
Source File: JwtTokenAuthMiddleware.scala From core with Apache License 2.0 | 5 votes |
package com.smartbackpackerapp.http.auth import cats.data.{EitherT, Kleisli, OptionT} import cats.effect.Sync import cats.syntax.applicativeError._ import cats.syntax.functor._ import com.smartbackpackerapp.http.auth.JwtTokenAuthMiddleware.AuthConfig import org.http4s.Credentials.Token import org.http4s.dsl.Http4sDsl import org.http4s.{AuthScheme, AuthedService, Request} import org.http4s.headers.Authorization import org.http4s.server.AuthMiddleware import tsec.jws.mac.JWTMac import tsec.mac.imports._ object JwtTokenAuthMiddleware { def apply[F[_] : Sync](apiToken: Option[String]): F[AuthMiddleware[F, String]] = new Middleware[F](apiToken).middleware case class AuthConfig(jwtKey: MacSigningKey[HMACSHA256]) } class Middleware[F[_]](apiToken: Option[String])(implicit F: Sync[F]) { private val ifEmpty = F.raiseError[AuthMiddleware[F, String]](new Exception("Api Token not found")) private def generateJwtKey(token: String): F[MacSigningKey[HMACSHA256]] = { F.catchNonFatal(HMACSHA256.buildKeyUnsafe(token.getBytes)) } val middleware: F[AuthMiddleware[F, String]] = apiToken.fold(ifEmpty) { token => generateJwtKey(token).map { jwtKey => val config = AuthConfig(jwtKey) new JwtTokenAuthMiddleware[F](config).middleware } } } class JwtTokenAuthMiddleware[F[_] : Sync](config: AuthConfig) extends Http4sDsl[F] { private val onFailure: AuthedService[String, F] = Kleisli(req => OptionT.liftF(Forbidden(req.authInfo))) private def bearerTokenFromRequest(request: Request[F]): OptionT[F, String] = OptionT.fromOption[F] { request.headers.get(Authorization).collect { case Authorization(Token(AuthScheme.Bearer, token)) => token } } private def verifyToken(request: Request[F], jwtKey: MacSigningKey[HMACSHA256]): OptionT[F, String] = for { token <- bearerTokenFromRequest(request) verified <- OptionT.liftF(JWTMac.verifyAndParse[F, HMACSHA256](token, jwtKey)) accessToken <- OptionT.fromOption[F](verified.body.subject) } yield accessToken private def authUser(jwtKey: MacSigningKey[HMACSHA256]): Kleisli[F, Request[F], Either[String, String]] = Kleisli { request => verifyToken(request, jwtKey).value.map { option => Either.cond[String, String](option.isDefined, option.get, "Unable to authorize token") }.recoverWith { case MacVerificationError(msg) => EitherT.leftT(msg).value } } def middleware: AuthMiddleware[F, String] = AuthMiddleware(authUser(config.jwtKey), onFailure) }
Example 8
Source File: DestinationInfoHttpEndpointSpec.scala From core with Apache License 2.0 | 5 votes |
package com.smartbackpackerapp.http import cats.Parallel import cats.syntax.option._ import com.smartbackpackerapp.common.TaskAssertion import com.smartbackpackerapp.config.SBConfiguration import com.smartbackpackerapp.http.Http4sUtils._ import com.smartbackpackerapp.model.{Country, CountryCode, CountryName, Currency, VisaNotRequired, VisaRequirementsData} import com.smartbackpackerapp.repository.algebra.VisaRequirementsRepository import com.smartbackpackerapp.service.{AbstractExchangeRateService, CurrencyExchangeDTO, DestinationInfoService} import monix.eval.Task import org.http4s.{HttpService, Query, Request, Status, Uri} import org.scalatest.prop.PropertyChecks import org.scalatest.{FlatSpecLike, Matchers} class DestinationInfoHttpEndpointSpec extends FlatSpecLike with Matchers with DestinationInfoHttpEndpointFixture { forAll(examples) { (from, to, expectedStatus, expectedCountry, expectedVisa) => it should s"retrieve visa requirements from $from to $to" in TaskAssertion { val request = Request[Task](uri = Uri(path = s"/$ApiVersion/traveling/$from/to/$to", query = Query(("baseCurrency", Some("EUR"))))) httpService(request).value.map { task => task.fold(fail("Empty response")){ response => response.status should be (expectedStatus) val body = response.body.asString assert(body.contains(expectedCountry)) assert(body.contains(expectedVisa)) } } } } } trait DestinationInfoHttpEndpointFixture extends PropertyChecks { val examples = Table( ("from", "code", "expectedStatus","expectedCountry", "expectedVisa"), ("AR", "GB", Status.Ok, "United Kingdom", "VisaNotRequired"), ("AR", "KO", Status.NotFound, "Country not found", """{"code":"100","error":"Country not found KO"}"""), ("AR", "AR", Status.BadRequest, "Countries must be different", """{"code":"101","error":"Countries must be different!"}""") ) private val repo = new VisaRequirementsRepository[Task] { override def findVisaRequirements(from: CountryCode, to: CountryCode): Task[Option[VisaRequirementsData]] = Task { if (to.value == "KO") none[VisaRequirementsData] else VisaRequirementsData( from = Country(CountryCode("AR"), CountryName("Argentina"), Currency("ARS")), to = Country(CountryCode("GB"), CountryName("United Kingdom"), Currency("GBP")), visaCategory = VisaNotRequired, description = "90 days within any 180 day period" ).some } } private lazy val sbConfig = new SBConfiguration[Task] private val rateService = new AbstractExchangeRateService[Task](sbConfig) { override protected def retrieveExchangeRate(uri: String): Task[CurrencyExchangeDTO] = Task { CurrencyExchangeDTO("EUR", "", Map("RON" -> 4.59)) } } private implicit val errorHandler = new HttpErrorHandler[Task] private implicit val parallel: Parallel[Task, Task] = Parallel[Task, Task.Par].asInstanceOf[Parallel[Task, Task]] val httpService: HttpService[Task] = taskMiddleware( new DestinationInfoHttpEndpoint( new DestinationInfoService[Task](sbConfig, repo, rateService) ).service ) }
Example 9
Source File: VisaRestrictionIndexHttpEndpointSpec.scala From core with Apache License 2.0 | 5 votes |
package com.smartbackpackerapp.http import cats.effect.IO import com.smartbackpackerapp.common.IOAssertion import com.smartbackpackerapp.model.{Count, CountryCode, Ranking, Sharing, VisaRestrictionsIndex} import com.smartbackpackerapp.repository.algebra.VisaRestrictionsIndexRepository import com.smartbackpackerapp.service.VisaRestrictionIndexService import org.http4s.{HttpService, Request, Status, Uri} import org.scalatest.prop.PropertyChecks import org.scalatest.{FlatSpecLike, Matchers} class VisaRestrictionIndexHttpEndpointSpec extends FlatSpecLike with Matchers with VisaRestrictionIndexFixture { forAll(examples) { (countryCode, expectedStatus) => it should s"try to retrieve visa restriction index for $countryCode" in IOAssertion { val request = Request[IO](uri = Uri(path = s"/$ApiVersion/ranking/$countryCode")) httpService(request).value.map { task => task.fold(fail("Empty response")){ response => response.status should be (expectedStatus) } } } } } trait VisaRestrictionIndexFixture extends PropertyChecks { import Http4sUtils._ private val repo = new VisaRestrictionsIndexRepository[IO] { override def findRestrictionsIndex(countryCode: CountryCode): IO[Option[VisaRestrictionsIndex]] = IO { if (countryCode.value == "AR") Some(VisaRestrictionsIndex(Ranking(0), Count(0), Sharing(0))) else None } } private implicit val errorHandler = new HttpErrorHandler[IO] val httpService: HttpService[IO] = ioMiddleware( new VisaRestrictionIndexHttpEndpoint( new VisaRestrictionIndexService[IO](repo) ).service ) val examples = Table( ("countryCode", "expectedStatus"), ("AR", Status.Ok), ("XX", Status.NotFound) ) }
Example 10
Source File: HealthInfoHttpEndpointSpec.scala From core with Apache License 2.0 | 5 votes |
package com.smartbackpackerapp.http import cats.effect.IO import com.smartbackpackerapp.common.IOAssertion import com.smartbackpackerapp.model.{CountryCode, Health, HealthAlert, HealthNotices, LevelOne, Vaccinations, Vaccine} import com.smartbackpackerapp.repository.algebra.HealthRepository import com.smartbackpackerapp.service.HealthService import org.http4s.{HttpService, Request, Status, Uri} import org.scalatest.prop.PropertyChecks import org.scalatest.{FlatSpecLike, Matchers} class HealthInfoHttpEndpointSpec extends FlatSpecLike with Matchers with HealthInfoFixture { forAll(examples) { (countryCode, expectedStatus) => it should s"try to retrieve health information for $countryCode" in IOAssertion { val request = Request[IO](uri = Uri(path = s"/$ApiVersion/health/$countryCode")) httpService(request).value.map { task => task.fold(fail("Empty response")){ response => response.status should be (expectedStatus) } } } } } trait HealthInfoFixture extends PropertyChecks { import Http4sUtils._ private val testHealth = Health( vaccinations = Vaccinations(List.empty[Vaccine], List.empty[Vaccine], List.empty[Vaccine]), notices = HealthNotices( alertLevel = LevelOne, alerts = List.empty[HealthAlert] ) ) private val repo = new HealthRepository[IO] { override def findHealthInfo(countryCode: CountryCode): IO[Option[Health]] = IO { if (countryCode.value == "AR") Some(testHealth) else None } } private implicit val errorHandler = new HttpErrorHandler[IO] val httpService: HttpService[IO] = ioMiddleware( new HealthInfoHttpEndpoint( new HealthService[IO](repo) ).service ) val examples = Table( ("countryCode", "expectedStatus"), ("AR", Status.Ok), ("XX", Status.NotFound) ) }
Example 11
Source File: AirlinesHttpEndpointSpec.scala From core with Apache License 2.0 | 5 votes |
package com.smartbackpackerapp.http import cats.effect.IO import com.smartbackpackerapp.common.IOAssertion import com.smartbackpackerapp.http.Http4sUtils._ import com.smartbackpackerapp.model._ import com.smartbackpackerapp.repository.algebra.AirlineRepository import com.smartbackpackerapp.service.AirlineService import org.http4s.{HttpService, Query, Request, Status, Uri} import org.scalatest.prop.PropertyChecks import org.scalatest.{FlatSpecLike, Matchers} class AirlinesHttpEndpointSpec extends FlatSpecLike with Matchers with AirlinesHttpEndpointFixture { forAll(examples) { (airline, expectedStatus, expectedBody) => it should s"find the airline $airline" in IOAssertion { val request = Request[IO](uri = Uri(path = s"/$ApiVersion/airlines", query = Query(("name", Some(airline))))) httpService(request).value.map { task => task.fold(fail("Empty response")){ response => response.status should be (expectedStatus) assert(response.body.asString.contains(expectedBody)) } } } } } trait AirlinesHttpEndpointFixture extends PropertyChecks { private val airlines: List[Airline] = List( Airline(AirlineName("Aer Lingus"), BaggagePolicy( allowance = List( BaggageAllowance(CabinBag, Some(10), BaggageSize(55, 40, 24)), BaggageAllowance(SmallBag, None, BaggageSize(25, 33, 20)) ), extra = None, website = Some("https://www.aerlingus.com/travel-information/baggage-information/cabin-baggage/")) ), Airline(AirlineName("Transavia"), BaggagePolicy( allowance = List( BaggageAllowance(CabinBag, None, BaggageSize(55, 40, 25)) ), extra = None, website = Some("https://www.transavia.com/en-EU/service/hand-luggage/")) ) ) private val testAirlineRepo = new AirlineRepository[IO] { override def findAirline(airlineName: AirlineName): IO[Option[Airline]] = IO { airlines.find(_.name.value == airlineName.value) } } private implicit val errorHandler = new HttpErrorHandler[IO] val httpService: HttpService[IO] = ioMiddleware( new AirlinesHttpEndpoint( new AirlineService[IO](testAirlineRepo) ).service ) val examples = Table( ("airline", "expectedStatus", "expectedBody"), ("Aer Lingus", Status.Ok, "baggagePolicy"), ("Transavia", Status.Ok, "baggagePolicy"), ("Ryan Air", Status.NotFound, """{"code":"100","error":"Airline not found Ryan Air"}""") ) }
Example 12
Source File: CountriesHttpEndpointSpec.scala From core with Apache License 2.0 | 5 votes |
package com.smartbackpackerapp.http import cats.effect.IO import com.smartbackpackerapp.common.IOAssertion import com.smartbackpackerapp.http.Http4sUtils._ import com.smartbackpackerapp.model.{Country, CountryCode, CountryName, Currency} import com.smartbackpackerapp.repository.algebra.CountryRepository import com.smartbackpackerapp.service.CountryService import org.http4s.{HttpService, Query, Request, Status, Uri} import org.scalatest.{FlatSpecLike, Matchers} class CountriesHttpEndpointSpec extends FlatSpecLike with Matchers with CountriesHttpEndpointFixture { it should s"find all the countries" in IOAssertion { val request = Request[IO](uri = Uri(path = s"/$ApiVersion/countries")) httpService(request).value.map { task => task.fold(fail("Empty response")){ response => response.status should be (Status.Ok) assert(response.body.asString.contains("Argentina")) } } } it should s"find all the schengen countries" in IOAssertion { val request = Request[IO](uri = Uri(path = s"/$ApiVersion/countries", query = Query(("query", Some("schengen"))))) httpService(request).value.map { task => task.fold(fail("Empty response")){ response => response.status should be (Status.Ok) assert(response.body.asString.contains("Poland")) } } } } trait CountriesHttpEndpointFixture { private val testCountries = List( Country(CountryCode("AR"), CountryName("Argentina"), Currency("ARS")) ) private val testSchengenCountries = List( Country(CountryCode("PL"), CountryName("Poland"), Currency("PLN")) ) private val repo = new CountryRepository[IO] { override def findAll: IO[List[Country]] = IO(testCountries) override def findSchengen: IO[List[Country]] = IO(testSchengenCountries) } private implicit val errorHandler = new HttpErrorHandler[IO] val httpService: HttpService[IO] = ioMiddleware( new CountriesHttpEndpoint[IO]( new CountryService[IO](repo) ).service ) }
Example 13
Source File: Http4sUtils.scala From core with Apache License 2.0 | 5 votes |
package com.smartbackpackerapp.http import cats.{Applicative, Monad} import cats.data.{Kleisli, OptionT} import cats.effect.IO import monix.eval.Task import monix.execution.Scheduler.Implicits.global import org.http4s.server.AuthMiddleware import org.http4s.{EntityBody, Request} import scala.concurrent.Await import scala.concurrent.duration.Duration object Http4sUtils { private def authUser[F[_]](implicit F: Applicative[F]): Kleisli[OptionT[F, ?], Request[F], String] = Kleisli(_ => OptionT.liftF(F.pure("access_token"))) def middleware[F[_]: Monad]: AuthMiddleware[F, String] = AuthMiddleware.apply[F, String](authUser) val taskMiddleware: AuthMiddleware[Task, String] = middleware[Task] val ioMiddleware: AuthMiddleware[IO, String] = middleware[IO] implicit class ByteVector2String(body: EntityBody[IO]) { def asString: String = { val array = body.compile.toVector.unsafeRunSync().toArray new String(array.map(_.toChar)) } } implicit class ByteVector2StringTask(body: EntityBody[Task]) { def asString: String = { val array = Await.result(body.compile.toVector.runAsync, Duration.Inf).toArray new String(array.map(_.toChar)) } } }
Example 14
Source File: AdserverHttpClientBuilder.scala From scala-openrtb with Apache License 2.0 | 5 votes |
package com.powerspace.openrtb.examples.rtb.http4s.adserver import com.google.openrtb.{BidRequest, BidResponse} import com.powerspace.openrtb.examples.rtb.http4s.common.ExampleSerdeModule import com.powerspace.openrtb.json.SerdeModule import io.circe.{Decoder, Encoder} import monix.eval.Task import org.http4s.Uri.{Authority, RegName, Scheme} import org.http4s.client.Client import org.http4s.{EntityDecoder, EntityEncoder, Method, Request, Uri} object AdserverHttpClientBuilder { import org.http4s.circe._ val serdeModule: SerdeModule = ExampleSerdeModule implicit val bidRequestEncoder: Encoder[BidRequest] = serdeModule.bidRequestEncoder implicit val bidRequestEntityEncoder: EntityEncoder[Task, BidRequest] = jsonEncoderOf[Task, BidRequest] implicit val bidResponseDecoder: Decoder[BidResponse] = serdeModule.bidResponseDecoder implicit val bidResponseEntityDecoder: EntityDecoder[Task, BidResponse] = jsonOf[Task, BidResponse] def bid(client: Client[Task], bidRequest: BidRequest): Task[Option[BidResponse]] = { val url = Uri( scheme = Some(Scheme.http), authority = Some(Authority(host = RegName("localhost"), port = Some(9000))), path = "/bid" ) val httpRequest = Request[Task]( method = Method.POST, uri = url ).withEntity[BidRequest](bidRequest) client.expectOption[BidResponse](httpRequest) } }
Example 15
Source File: AbstractHttpClient.scala From nexus with Apache License 2.0 | 5 votes |
package ch.epfl.bluebrain.nexus.cli.clients import cats.effect.{Sync, Timer} import cats.implicits._ import ch.epfl.bluebrain.nexus.cli.CliError.ClientError import ch.epfl.bluebrain.nexus.cli.CliError.ClientError.{SerializationError, Unexpected} import ch.epfl.bluebrain.nexus.cli.config.EnvConfig import ch.epfl.bluebrain.nexus.cli.{logRetryErrors, ClientErrOr, Console} import io.circe.Decoder import org.http4s.circe.CirceEntityDecoder._ import org.http4s.client.Client import org.http4s.{Request, Response} import retry.CatsEffect._ import retry.RetryPolicy import retry.syntax.all._ import scala.reflect.ClassTag import scala.util.control.NonFatal class AbstractHttpClient[F[_]: Timer](client: Client[F], env: EnvConfig)(implicit protected val F: Sync[F], protected val console: Console[F] ) { protected val retry = env.httpClient.retry protected def successCondition[A] = retry.condition.notRetryFromEither[A] _ implicit protected val retryPolicy: RetryPolicy[F] = retry.retryPolicy implicit protected def logOnError[A] = logRetryErrors[F, A]("interacting with an HTTP API") protected def executeDiscard[A](req: Request[F], returnValue: => A): F[ClientErrOr[A]] = execute(req, _.body.compile.drain.as(Right(returnValue))) protected def executeParse[A: Decoder](req: Request[F])(implicit A: ClassTag[A]): F[ClientErrOr[A]] = execute( req, _.attemptAs[A].value.map( _.leftMap(err => SerializationError(err.message, s"The response payload was not of type '${A.runtimeClass.getSimpleName}'") ) ) ) private def execute[A](req: Request[F], f: Response[F] => F[ClientErrOr[A]]): F[ClientErrOr[A]] = client .fetch(req)(ClientError.errorOr[F, A](r => f(r))) .recoverWith { case NonFatal(err) => F.delay(Left(Unexpected(Option(err.getMessage).getOrElse("").take(30)))) } .retryingM(successCondition[A]) }
Example 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: 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 18
Source File: Http4sRequestToRawBody.scala From tapir with Apache License 2.0 | 5 votes |
package sttp.tapir.server.http4s import java.io.ByteArrayInputStream import cats.effect.{Blocker, ContextShift, Sync} import cats.implicits._ import fs2.Chunk import org.http4s.headers.{`Content-Disposition`, `Content-Type`} import org.http4s.{Charset, EntityDecoder, Request, multipart} import sttp.model.{Header, Part} import sttp.tapir.{RawPart, RawBodyType} class Http4sRequestToRawBody[F[_]: Sync: ContextShift](serverOptions: Http4sServerOptions[F]) { def apply[R](body: fs2.Stream[F, Byte], bodyType: RawBodyType[R], charset: Option[Charset], req: Request[F]): F[R] = { def asChunk: F[Chunk[Byte]] = body.compile.to(Chunk) def asByteArray: F[Array[Byte]] = body.compile.to(Chunk).map(_.toByteBuffer.array()) bodyType match { case RawBodyType.StringBody(defaultCharset) => asByteArray.map(new String(_, charset.map(_.nioCharset).getOrElse(defaultCharset))) case RawBodyType.ByteArrayBody => asByteArray case RawBodyType.ByteBufferBody => asChunk.map(_.toByteBuffer) case RawBodyType.InputStreamBody => asByteArray.map(new ByteArrayInputStream(_)) case RawBodyType.FileBody => serverOptions.createFile(serverOptions.blockingExecutionContext, req).flatMap { file => val fileSink = fs2.io.file.writeAll(file.toPath, Blocker.liftExecutionContext(serverOptions.blockingExecutionContext)) body.through(fileSink).compile.drain.map(_ => file) } case m: RawBodyType.MultipartBody => // TODO: use MultipartDecoder.mixedMultipart once available? implicitly[EntityDecoder[F, multipart.Multipart[F]]].decode(req, strict = false).value.flatMap { case Left(failure) => throw new IllegalArgumentException("Cannot decode multipart body: " + failure) // TODO case Right(mp) => val rawPartsF: Vector[F[RawPart]] = mp.parts .flatMap(part => part.name.flatMap(name => m.partType(name)).map((part, _)).toList) .map { case (part, codecMeta) => toRawPart(part, codecMeta, req).asInstanceOf[F[RawPart]] } val rawParts: F[Vector[RawPart]] = rawPartsF.sequence rawParts.asInstanceOf[F[R]] // R is Seq[RawPart] } } } private def toRawPart[R](part: multipart.Part[F], partType: RawBodyType[R], req: Request[F]): F[Part[R]] = { val dispositionParams = part.headers.get(`Content-Disposition`).map(_.parameters).getOrElse(Map.empty) val charset = part.headers.get(`Content-Type`).flatMap(_.charset) apply(part.body, partType, charset, req) .map(r => Part( part.name.getOrElse(""), r, otherDispositionParams = dispositionParams - Part.NameDispositionParam, headers = part.headers.toList.map(h => Header(h.name.value, h.value)) ) ) } }
Example 19
Source File: Http4sServerOptions.scala From tapir with Apache License 2.0 | 5 votes |
package sttp.tapir.server.http4s import java.io.File import cats.Applicative import cats.effect.{ContextShift, Sync} import org.http4s.Request import sttp.tapir.Defaults import sttp.tapir.server.{DecodeFailureHandler, LogRequestHandling, ServerDefaults} import scala.concurrent.ExecutionContext case class Http4sServerOptions[F[_]]( createFile: (ExecutionContext, Request[F]) => F[File], // TODO: include request/part headers, information if this is a part? blockingExecutionContext: ExecutionContext, ioChunkSize: Int, decodeFailureHandler: DecodeFailureHandler, logRequestHandling: LogRequestHandling[F[Unit]] ) object Http4sServerOptions { implicit def default[F[_]: Sync: ContextShift]: Http4sServerOptions[F] = Http4sServerOptions( defaultCreateFile, ExecutionContext.Implicits.global, 8192, ServerDefaults.decodeFailureHandler, defaultLogRequestHandling[F] ) def defaultCreateFile[F[_]](implicit sync: Sync[F], cs: ContextShift[F]): (ExecutionContext, Request[F]) => F[File] = (ec, _) => cs.evalOn(ec)(sync.delay(Defaults.createTempFile())) def defaultLogRequestHandling[F[_]: Sync]: LogRequestHandling[F[Unit]] = LogRequestHandling[F[Unit]]( doLogWhenHandled = debugLog[F], doLogAllDecodeFailures = debugLog[F], doLogLogicExceptions = (msg: String, ex: Throwable) => Sync[F].delay(EndpointToHttp4sServer.log.error(ex)(msg)), noLog = Applicative[F].unit ) private def debugLog[F[_]: Sync](msg: String, exOpt: Option[Throwable]): F[Unit] = exOpt match { case None => Sync[F].delay(EndpointToHttp4sServer.log.debug(msg)) case Some(ex) => Sync[F].delay(EndpointToHttp4sServer.log.debug(ex)(msg)) } }
Example 20
Source File: Http4sDecodeInputsContext.scala From tapir with Apache License 2.0 | 5 votes |
package sttp.tapir.server.http4s import org.http4s.Request import org.http4s.util.CaseInsensitiveString import sttp.model.{Method, QueryParams} import sttp.tapir.model.ServerRequest import sttp.tapir.server.internal.DecodeInputsContext class Http4sDecodeInputsContext[F[_]](req: Request[F]) extends DecodeInputsContext { override def method: Method = Method(req.method.name.toUpperCase) override def nextPathSegment: (Option[String], DecodeInputsContext) = { val nextStart = req.pathInfo.dropWhile(_ == '/') val segment = nextStart.split("/", 2) match { case Array("") => None case Array(s) => Some(s) case Array(s, _) => Some(s) } // if the routes are mounted within a context (e.g. using a router), we have to match against what comes // after the context. This information is stored in the the PathInfoCaret attribute val oldCaret = req.attributes.lookup(Request.Keys.PathInfoCaret).getOrElse(0) val segmentSlashLength = segment.map(_.length).getOrElse(0) + 1 val reqWithNewCaret = req.withAttribute(Request.Keys.PathInfoCaret, oldCaret + segmentSlashLength) (segment, new Http4sDecodeInputsContext(reqWithNewCaret)) } override def header(name: String): List[String] = req.headers.get(CaseInsensitiveString(name)).map(_.value).toList override def headers: Seq[(String, String)] = req.headers.toList.map(h => (h.name.value, h.value)) override def queryParameter(name: String): Seq[String] = queryParameters.getMulti(name).getOrElse(Nil) override val queryParameters: QueryParams = QueryParams.fromMultiMap(req.multiParams) override def bodyStream: Any = req.body override def serverRequest: ServerRequest = new Http4sServerRequest(req) }
Example 21
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 22
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 23
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 24
Source File: KmsCommand.scala From aws4s with MIT License | 5 votes |
package org.aws4s.kms import cats.effect.Effect import cats.implicits._ import io.circe.{Decoder, Json} import org.aws4s.core.ExtraEntityDecoderInstances._ import org.aws4s._ import org.aws4s.core.{Command, CommandPayload, RenderedParam, ServiceName} import org.http4s.circe._ import org.http4s.headers.{Host, `Content-Type`} import org.http4s.{Header, Headers, MediaType, Method, Request, Uri} private[kms] abstract class KmsCommand[F[_]: Effect, R: Decoder] extends Command[F, Json, R] { override def serviceName: ServiceName = ServiceName.Kms override def payloadSigning: PayloadSigning = PayloadSigning.Signed def action: String override final val requestGenerator: List[RenderedParam[Json]] => F[Request[F]] = { params => val host = s"kms.${region.name}.amazonaws.com" val payload: Json = CommandPayload.jsonObject(params) Request[F]( Method.POST, Uri.unsafeFromString(s"https://$host/"), headers = Headers(Header("X-Amz-Target", s"TrentService.$action"), Host(host)) ).withBody(payload).map(_.withContentType(`Content-Type`.apply(MediaType.fromKey(("application", "x-amz-json-1.1"))))) } }
Example 25
Source File: SqsCommand.scala From aws4s with MIT License | 5 votes |
package org.aws4s.sqs import cats.effect.Effect import org.http4s.headers.Host import org.http4s.{EntityDecoder, Headers, Method, Request, UrlForm} import org.aws4s._ import org.aws4s.core.Command.Validator import org.aws4s.core.{Command, RenderedParam, ServiceName} private[sqs] abstract class SqsCommand[F[_]: Effect, R: EntityDecoder[F, ?]] extends Command[F, String, R] { val q: Queue val action: String override final val serviceName: ServiceName = ServiceName.Sqs override final val payloadSigning: PayloadSigning = PayloadSigning.Signed override final val region: Region = q.region override final val validator: Validator[String] = _ => None override final val requestGenerator: List[RenderedParam[String]] => F[Request[F]] = { params => val body = params.map(p => (p.name, p.value)).foldLeft(UrlForm())((form, newPair) => form + newPair) + ("Action" -> action) Request[F](Method.POST, q.uri, headers = Headers(Host(q.host))).withBody[UrlForm](body) } }
Example 26
Source File: DynamoDbCommand.scala From aws4s with MIT License | 5 votes |
package org.aws4s.dynamodb import cats.effect.Effect import io.circe.{Decoder, Json} import org.aws4s.PayloadSigning import org.http4s.headers.{Host, `Content-Type`} import org.http4s.{Header, Headers, MediaType, Method, Request, Uri} import org.http4s.circe._ import cats.implicits._ import org.aws4s.core.ExtraEntityDecoderInstances._ import org.aws4s.core.{Command, CommandPayload, RenderedParam, ServiceName} private[dynamodb] abstract class DynamoDbCommand[F[_]: Effect, R: Decoder] extends Command[F, Json, R] { override def serviceName: ServiceName = ServiceName.DynamoDb override def payloadSigning: PayloadSigning = PayloadSigning.Signed def action: String override final val requestGenerator: List[RenderedParam[Json]] => F[Request[F]] = params => { val host = s"dynamodb.${region.name}.amazonaws.com" val payload: Json = CommandPayload.jsonObject(params) Request[F]( Method.POST, Uri.unsafeFromString(s"https://$host/"), headers = Headers(Header("X-Amz-Target", s"DynamoDB_20120810.$action"), Host(host)) ).withBody(payload).map(_.withContentType(`Content-Type`.apply(MediaType.fromKey(("application", "x-amz-json-1.0"))))) } }
Example 27
Source File: S3ObjectCommand.scala From aws4s with MIT License | 5 votes |
package org.aws4s.s3 import cats.effect.Effect import org.aws4s.core.{Command, Param, RenderedParam, ServiceName} import org.http4s.{EntityDecoder, Headers, Method, Request, Uri} import fs2._ import org.http4s.headers.Host import cats.implicits._ private[aws4s] abstract class S3ObjectCommand[F[_]: Effect, R: EntityDecoder[F, ?]] extends Command[F, Nothing, R] { override final val serviceName: ServiceName = ServiceName.S3 val action: Method val bucketName: BucketName val objectPath: ObjectPath val payload: F[Stream[F, Byte]] override final val params: List[Param[Nothing]] = List.empty override final val validator: Command.Validator[Nothing] = _ => None override final val requestGenerator: List[RenderedParam[Nothing]] => F[Request[F]] = { _ => val host = s"${bucketName.value}.s3.${region.name}.amazonaws.com" val uri = Uri.unsafeFromString(s"https://$host/").withPath(objectPath.value) for { pStream <- payload pBytes <- pStream.compile.toVector r <- Request[F](action, uri, headers = Headers(Host(host))).withBody(pBytes.toArray) } yield r } }
Example 28
Source File: S3ServiceCommand.scala From aws4s with MIT License | 5 votes |
package org.aws4s.s3 import cats.effect.Effect import cats.implicits._ import fs2._ import org.aws4s.PayloadSigning import org.aws4s.core.{Command, Param, RenderedParam, ServiceName} import org.http4s.headers.Host import org.http4s.{EntityDecoder, Headers, Method, Request, Uri} private[aws4s] abstract class S3ServiceCommand[F[_]: Effect, R: EntityDecoder[F, ?]] extends Command[F, Nothing, R] { override final val serviceName: ServiceName = ServiceName.S3 val action: Method val payload: F[Stream[F, Byte]] override final val payloadSigning: PayloadSigning = PayloadSigning.Signed override final val params: List[Param[Nothing]] = List.empty override final val validator: Command.Validator[Nothing] = _ => None override final val requestGenerator: List[RenderedParam[Nothing]] => F[Request[F]] = { _ => val host = s"s3.${region.name}.amazonaws.com" val uri = Uri.unsafeFromString(s"https://$host/").withPath("/") payload map { p => Request[F](action, uri, headers = Headers(Host(host))).withBodyStream(p) } } }
Example 29
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 30
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 31
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 32
Source File: MavenCentralClient.scala From zorechka-bot with MIT License | 5 votes |
package com.wix.zorechka.clients import com.wix.zorechka.Dep import org.http4s.{EntityDecoder, Header, Headers, Method, Request, Uri} import zio.{Task, ZIO} import zio.interop.catz._ import io.circe.generic.auto._ import org.http4s.circe.jsonOf import org.http4s.client.Client trait MavenCentralClient { val client: MavenCentralClient.Service } object MavenCentralClient { trait Service { def allVersions(dep: Dep): Task[List[Dep]] } trait Live extends MavenCentralClient { protected val httpClient: Client[Task] val client = new MavenCentralClient.Service { case class Response(response: InnerResponse) case class InnerResponse(docs: Seq[Document]) case class Document(v: String) implicit val decoder: EntityDecoder[Task, Response] = jsonOf[Task, Response] override def allVersions(dep: Dep): Task[List[Dep]] = { ZIO.accessM { client => val uri = Uri .unsafeFromString("http://search.maven.org/solrsearch/select") .withQueryParam("rows", "10") .withQueryParam("core", "gav") .withQueryParam("q", s""" g:"${dep.groupId}" AND a:"${dep.artifactId}" """) println(s"Maven search: ${uri.renderString}") val request = Request[Task](Method.GET, uri, headers = Headers.of(Header("Accept", "application/json"))) httpClient.fetch(request)(response => response.as[Response]).map { _.response.docs.map(_.v).map(v => Dep(dep.groupId, dep.artifactId, v)).toList } } } } } }
Example 33
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 34
Source File: HttpAttributes.scala From opencensus-scala with Apache License 2.0 | 5 votes |
package io.opencensus.scala.http4s import io.opencensus.scala.http.{RequestExtractor, ResponseExtractor} import org.http4s.util.CaseInsensitiveString import org.http4s.{Request, Response} private[http4s] object HttpAttributes { implicit def requestExtractor[F[_]]: RequestExtractor[Request[F]] = new RequestExtractor[Request[F]] { override def method(req: Request[F]): String = req.method.name override def userAgent(req: Request[F]): Option[String] = req.headers.get(CaseInsensitiveString("User-Agent")).map(_.value) override def path(req: Request[F]): String = req.uri.path.toString override def host(req: Request[F]): String = { val hostHeader = req.headers .get(CaseInsensitiveString("Host")) .map(_.value) req.uri.authority .map(_.host.value) .getOrElse( hostHeader // Having no Host header with a relative URL is invalid according to rfc2616, // but http4s still allows to create such HttpRequests. .getOrElse("") ) } } implicit def responseExtractor[F[_]]: ResponseExtractor[Response[F]] = (res: Response[F]) => res.status.code.toLong }
Example 35
Source File: TracingClient.scala From opencensus-scala with Apache License 2.0 | 5 votes |
package io.opencensus.scala.http4s import cats.effect.{Effect, Resource} import cats.implicits._ import io.opencensus.scala.Tracing import io.opencensus.scala.http.propagation.Propagation import io.opencensus.scala.http.{HttpAttributes => BaseHttpAttributes} import io.opencensus.scala.http4s.HttpAttributes._ import io.opencensus.scala.http4s.TracingUtils.recordResponse import io.opencensus.scala.http4s.propagation.Http4sFormatPropagation import io.opencensus.trace.{Span, Status} import org.http4s.client.Client import org.http4s.{Header, Request, Response} abstract class TracingClient[F[_]: Effect] { protected val tracing: Tracing protected val propagation: Propagation[Header, Request[F]] def trace(client: Client[F], parentSpan: Option[Span] = None): Client[F] = { val tracedOpen: Request[F] => Resource[F, Response[F]] = req => for { span <- Resource.liftF(startSpan(parentSpan, req)) enrichedReq = addTraceHeaders(req, span) res <- client .run(enrichedReq) .onError(traceError(span).andThen(x => Resource.liftF(x))) } yield recordResponse(span, tracing)(res) Client(tracedOpen) } private def traceError(span: Span): PartialFunction[Throwable, F[Unit]] = { case _ => recordException(span) } private def startSpan(parentSpan: Option[Span], req: Request[F]) = Effect[F].delay(startAndEnrichSpan(req, parentSpan)) private def startAndEnrichSpan( req: Request[F], parentSpan: Option[Span] ): Span = { val name = req.uri.path.toString val span = parentSpan.fold(tracing.startSpan(name))(span => tracing.startSpanWithParent(name, span) ) BaseHttpAttributes.setAttributesForRequest(span, req) span } private def addTraceHeaders(request: Request[F], span: Span): Request[F] = request.withHeaders( request.headers.put(propagation.headersWithTracingContext(span): _*) ) private def recordException(span: Span) = Effect[F].delay(tracing.endSpan(span, Status.INTERNAL)) } object TracingClient { def apply[F[_]: Effect]: TracingClient[F] = new TracingClient[F] { override protected val tracing: Tracing = Tracing override protected val propagation: Propagation[Header, Request[F]] = new Http4sFormatPropagation[F] {} } }
Example 36
Source File: Http4sAttributesSpec.scala From opencensus-scala with Apache License 2.0 | 5 votes |
package io.opencensus.scala.http4s import cats.Id import io.opencensus.scala.http.HttpAttributesSpec import org.http4s.{Header, Headers, Request, Response, Status, Uri} class Http4sAttributesSpec extends HttpAttributesSpec { import HttpAttributes._ "Http4s attributes extraction" should behave like httpAttributes( request, response ) def request: BuildRequest => Request[Id] = (request: BuildRequest) => Request[Id]( uri = Uri.unsafeFromString(request.host ++ request.path), headers = Headers( List(Header("User-Agent", request.userAgent)) ++ request.hostHeader .map(Header("Host", _)) ) ) def response: Int => Response[Id] = (code: Int) => Response[Id](Status(code)) }
Example 37
Source File: Github.scala From sonar-scala with GNU Lesser General Public License v3.0 | 5 votes |
package com.mwz.sonar.scala package pr package github import cats.effect.Sync import cats.syntax.flatMap._ import com.mwz.sonar.scala.pr.github.Codec._ import io.circe.generic.auto._ import mouse.boolean._ import org.http4s.client.Client import org.http4s.{Header, Headers, Method, Request, Uri} trait Github[F[_]] { def authenticatedUser: F[User] def pullRequest: F[PullRequest] def comments: F[List[Comment]] def createComment(comment: NewComment): F[Unit] def files: F[List[File]] def createStatus(sha: String, status: NewStatus): F[Unit] } object Github { def apply[F[_]: Sync](client: Client[F], pr: GlobalConfig.PullRequest): Github[F] = new Github[F] { val auth: Header = Header("Authorization", s"token ${pr.github.oauth}") val userUri: Uri = pr.github.apiuri / "user" val prUri: Uri = (pr.github.apiuri / "repos").addPath(pr.github.repository) / "pulls" / pr.prNumber val commentsUri: Uri = prUri / "comments" val filesUri: Uri = prUri / "files" def newStatusUri(sha: String): Uri = (pr.github.apiuri / "repos").addPath(pr.github.repository) / "statuses" / sha def request(uri: Uri): Request[F] = { Request[F]( uri = uri, headers = Headers.of(auth) ) } def authenticatedUser: F[User] = client.expect[User](request(userUri)) def pullRequest: F[PullRequest] = client.expect[PullRequest](request(prUri)) def comments: F[List[Comment]] = client.expect[List[Comment]](request(commentsUri)) def createComment(comment: NewComment): F[Unit] = { val request: F[Request[F]] = Sync[F].pure( Request(Method.POST, commentsUri, headers = Headers.of(auth)) .withEntity(comment) ) pr.dryRun.fold(Sync[F].unit, client.expect[Comment](request) >> Sync[F].unit) } def files: F[List[File]] = client.expect[List[File]](request(filesUri)) def createStatus(sha: String, status: NewStatus): F[Unit] = { val request: F[Request[F]] = Sync[F].pure( Request(Method.POST, newStatusUri(sha), headers = Headers.of(auth)) .withEntity(status) ) pr.dryRun.fold(Sync[F].unit, client.expect[Status](request) >> Sync[F].unit) } } }
Example 38
Source File: JdkHttpClient.scala From http4s-jdk-http-client with Apache License 2.0 | 5 votes |
package org.http4s.client.jdkhttpclient import java.net.URI import java.net.http.HttpRequest.BodyPublishers import java.net.http.HttpResponse.BodyHandlers import java.net.http.{HttpClient, HttpRequest, HttpResponse} import java.nio.ByteBuffer import java.util import java.util.concurrent.Flow import cats.ApplicativeError import cats.effect._ import cats.implicits._ import fs2.concurrent.SignallingRef import fs2.interop.reactivestreams._ import fs2.{Chunk, Stream} import org.http4s.client.Client import org.http4s.client.jdkhttpclient.compat.CollectionConverters._ import org.http4s.internal.fromCompletionStage import org.http4s.util.CaseInsensitiveString import org.http4s.{Header, Headers, HttpVersion, Request, Response, Status} import org.reactivestreams.FlowAdapters object JdkHttpClient { def simple[F[_]](implicit F: ConcurrentEffect[F], CS: ContextShift[F]): F[Client[F]] = F.delay(HttpClient.newHttpClient()).map(apply(_)) def convertHttpVersionFromHttp4s[F[_]]( version: HttpVersion )(implicit F: ApplicativeError[F, Throwable]): F[HttpClient.Version] = version match { case HttpVersion.`HTTP/1.1` => HttpClient.Version.HTTP_1_1.pure[F] case HttpVersion.`HTTP/2.0` => HttpClient.Version.HTTP_2.pure[F] case _ => F.raiseError(new IllegalArgumentException("invalid HTTP version")) } // see jdk.internal.net.http.common.Utils#DISALLOWED_HEADERS_SET private val restrictedHeaders = Set( "connection", "content-length", "date", "expect", "from", "host", "upgrade", "via", "warning" ).map(CaseInsensitiveString(_)) }
Example 39
Source File: package.scala From tsec with MIT License | 5 votes |
package tsec import cats.data.OptionT import org.bouncycastle.util.encoders.Hex import org.http4s.server.Middleware import org.http4s.{Request, Response} import tsec.common.ManagedRandom package object csrf { type CSRFToken = CSRFToken.Token object CSRFToken extends ManagedRandom { type Token <: String def apply(s: String): CSRFToken = s.asInstanceOf[CSRFToken] def subst[F[_]](value: F[String]): F[CSRFToken] = value.asInstanceOf[F[CSRFToken]] def generateHexBase(tokenLength: Int = 32): String = { val tokenBytes = new Array[Byte](tokenLength) nextBytes(tokenBytes) Hex.toHexString(tokenBytes) } } type CSRFMiddleware[F[_]] = Middleware[OptionT[F, ?], Request[F], Response[F], Request[F], Response[F]] }
Example 40
Source File: SignedCookieAuthenticatorTests.scala From tsec with MIT License | 5 votes |
package tsec.authentication import java.time.Instant import java.util.UUID import cats.effect.IO import org.http4s.{Request, RequestCookie} import tsec.keygen.symmetric.IdKeyGen import tsec.mac.MessageAuth import tsec.mac.jca._ import scala.concurrent.duration._ class SignedCookieAuthenticatorTests extends RequestAuthenticatorSpec { private val cookieName = "hi" implicit def cookieBackingStore[A] = dummyBackingStore[IO, UUID, AuthenticatedCookie[A, Int]](_.id) def genAuthenticator[A]( implicit keyGenerator: IdKeyGen[A, MacSigningKey], store: BackingStore[IO, UUID, AuthenticatedCookie[A, Int]], M: MessageAuth[IO, A, MacSigningKey] ): AuthSpecTester[AuthenticatedCookie[A, Int]] = { val dummyStore = dummyBackingStore[IO, Int, DummyUser](_.id) val authenticator = SignedCookieAuthenticator[IO, Int, DummyUser, A]( TSecCookieSettings(cookieName, false, expiryDuration = 10.minutes, maxIdle = Some(10.minutes)), store, dummyStore, keyGenerator.generateKey ) new AuthSpecTester[AuthenticatedCookie[A, Int]](authenticator, dummyStore) { def embedInRequest(request: Request[IO], authenticator: AuthenticatedCookie[A, Int]): Request[IO] = { val cookie = authenticator.toCookie request.addCookie(RequestCookie(cookie.name, cookie.content)) } def expireAuthenticator(b: AuthenticatedCookie[A, Int]): IO[AuthenticatedCookie[A, Int]] = { val now = Instant.now() val updated = b.copy[A, Int](expiry = now.minusSeconds(2000)) store.update(updated).map(_ => updated) } def timeoutAuthenticator(b: AuthenticatedCookie[A, Int]): IO[AuthenticatedCookie[A, Int]] = { val now = Instant.now() val updated = b.copy[A, Int](lastTouched = Some(now.minusSeconds(2000))) store.update(updated).map(_ => updated) } def wrongKeyAuthenticator: IO[AuthenticatedCookie[A, Int]] = SignedCookieAuthenticator[IO, Int, DummyUser, A]( TSecCookieSettings(cookieName, false, expiryDuration = 10.minutes, maxIdle = Some(10.minutes)), store, dummyStore, keyGenerator.generateKey ).create(123) } } def CookieAuthTest[A](string: String, auth: AuthSpecTester[AuthenticatedCookie[A, Int]]) = AuthenticatorTest[AuthenticatedCookie[A, Int]](string, auth) def CookieReqTest[A](string: String, auth: AuthSpecTester[AuthenticatedCookie[A, Int]]) = requestAuthTests[AuthenticatedCookie[A, Int]](string, auth) CookieAuthTest[HMACSHA1]("HMACSHA1 Authenticator", genAuthenticator[HMACSHA1]) CookieAuthTest[HMACSHA256]("HMACSHA256 Authenticator", genAuthenticator[HMACSHA256]) CookieAuthTest[HMACSHA384]("HMACSHA384 Authenticator", genAuthenticator[HMACSHA384]) CookieAuthTest[HMACSHA512]("HMACSHA512 Authenticator", genAuthenticator[HMACSHA512]) CookieReqTest[HMACSHA1]("HMACSHA1 Authenticator", genAuthenticator[HMACSHA1]) CookieReqTest[HMACSHA256]("HMACSHA256 Authenticator", genAuthenticator[HMACSHA256]) CookieReqTest[HMACSHA384]("HMACSHA384 Authenticator", genAuthenticator[HMACSHA384]) CookieReqTest[HMACSHA512]("HMACSHA512 Authenticator", genAuthenticator[HMACSHA512]) def signedCookieTests[A]( auth: AuthSpecTester[AuthenticatedCookie[A, Int]] )(implicit M: MessageAuth[IO, A, MacSigningKey]) = { behavior of "Signed Cookie Authenticator " + M.algorithm it should "expire tokens on discard" in { val program: IO[Boolean] = for { cookie <- auth.auth.create(0) expired <- auth.auth.discard(cookie) now <- IO(Instant.now()) } yield SignedCookieAuthenticator.isExpired(expired, now, None) program.unsafeRunSync() mustBe false } } signedCookieTests[HMACSHA1](genAuthenticator[HMACSHA1]) signedCookieTests[HMACSHA256](genAuthenticator[HMACSHA256]) signedCookieTests[HMACSHA384](genAuthenticator[HMACSHA384]) signedCookieTests[HMACSHA512](genAuthenticator[HMACSHA512]) }
Example 41
Source File: BearerTokenAuthenticatorTests.scala From tsec with MIT License | 5 votes |
package tsec.authentication import java.time.Instant import cats.effect.IO import org.http4s.headers.Authorization import org.http4s.{AuthScheme, Credentials, Request} import tsec.common.SecureRandomId import scala.concurrent.duration._ class BearerTokenAuthenticatorTests extends RequestAuthenticatorSpec { def timeoutAuthSpecTester: AuthSpecTester[TSecBearerToken[Int]] = { val tokenStore: BackingStore[IO, SecureRandomId, TSecBearerToken[Int]] = dummyBackingStore[IO, SecureRandomId, TSecBearerToken[Int]](s => SecureRandomId.coerce(s.id)) val dummyStore = dummyBackingStore[IO, Int, DummyUser](_.id) val settings = TSecTokenSettings(10.minutes, Some(10.minutes)) val authenticator = BearerTokenAuthenticator(tokenStore, dummyStore, settings) new AuthSpecTester[TSecBearerToken[Int]](authenticator, dummyStore) { def embedInRequest(request: Request[IO], authenticator: TSecBearerToken[Int]): Request[IO] = request.putHeaders(Authorization(Credentials.Token(AuthScheme.Bearer, authenticator.id))) def expireAuthenticator(b: TSecBearerToken[Int]): IO[TSecBearerToken[Int]] = authenticator.update(b.copy(expiry = Instant.now.minusSeconds(30))) def timeoutAuthenticator(b: TSecBearerToken[Int]): IO[TSecBearerToken[Int]] = authenticator.update(b.copy(lastTouched = Some(Instant.now.minusSeconds(300000)))) def wrongKeyAuthenticator: IO[TSecBearerToken[Int]] = IO.pure(TSecBearerToken(SecureRandomId.Interactive.generate, -20, Instant.now(), None)) } } AuthenticatorTest("Bearer token authenticator", timeoutAuthSpecTester) requestAuthTests[TSecBearerToken[Int]]("Bearer token Request handler", timeoutAuthSpecTester) }
Example 42
Source File: KamonSupport.scala From kamon-http4s with Apache License 2.0 | 5 votes |
package kamon.http4s package middleware.client import cats.effect.{Effect, Resource} import cats.implicits._ import com.typesafe.config.Config import kamon.Kamon import kamon.context.Context import kamon.instrumentation.http.HttpClientInstrumentation import org.http4s.{Request, Response} import org.http4s.client.Client object KamonSupport { private var _instrumentation = instrumentation(Kamon.config()) private def instrumentation(kamonConfig: Config): HttpClientInstrumentation = { val httpClientConfig = kamonConfig.getConfig("kamon.instrumentation.http4s.client") HttpClientInstrumentation.from(httpClientConfig, "http4s.client") } Kamon.onReconfigure(newConfig => _instrumentation = instrumentation(newConfig)) def apply[F[_]](underlying: Client[F])(implicit F:Effect[F]): Client[F] = Client { request => for { ctx <- Resource.liftF(F.delay(Kamon.currentContext())) k <- kamonClient(underlying)(request)(ctx)(_instrumentation) } yield k } private def kamonClient[F[_]](underlying: Client[F]) (request: Request[F]) (ctx: Context) (instrumentation: HttpClientInstrumentation) (implicit F:Effect[F]): Resource[F, Response[F]] = for { requestHandler <- Resource.liftF(F.delay(instrumentation.createHandler(getRequestBuilder(request), ctx))) response <- underlying.run(requestHandler.request).attempt trackedResponse <- Resource.liftF(handleResponse(response, requestHandler, instrumentation.settings)) } yield trackedResponse def handleResponse[F[_]]( response: Either[Throwable, Response[F]], requestHandler: HttpClientInstrumentation.RequestHandler[Request[F]], settings: HttpClientInstrumentation.Settings )(implicit F:Effect[F]): F[Response[F]] = response match { case Right(res) => if(res.status.code == 404) requestHandler.span.name(settings.defaultOperationName) requestHandler.processResponse(getResponseBuilder(res)) F.delay(res) case Left(error) => requestHandler.span.fail(error).finish() F.raiseError(error) } }
Example 43
Source File: Http4sRpcTransport.scala From iotchain with MIT License | 5 votes |
package jbok.network.rpc.http import cats.effect.ConcurrentEffect import jbok.network.rpc.{RpcRequest, RpcResponse, RpcTransport} import org.http4s.client.blaze.BlazeClientBuilder import org.http4s.{EntityDecoder, EntityEncoder, Method, Request, Uri} import scala.concurrent.ExecutionContext final class Http4sRpcTransport[F[_], P]( baseUri: Uri )(implicit F: ConcurrentEffect[F], entityEncoder: EntityEncoder[F, P], entityDecoder: EntityDecoder[F, RpcResponse[P]]) extends RpcTransport[F, P] { override def fetch(request: RpcRequest[P]): F[RpcResponse[P]] = { val uri = request.path.foldLeft(baseUri)(_ / _) val req = Request[F](Method.POST, uri = uri).withEntity(request.payload) BlazeClientBuilder[F](ExecutionContext.global).resource.use { client => client.fetchAs[RpcResponse[P]](req) } } }
Example 44
Source File: HttpUploadTest.scala From temperature-machine with Apache License 2.0 | 5 votes |
package bad.robot.temperature.client import java.net.InetAddress import bad.robot.temperature.rrd.{Host, Seconds} import bad.robot.temperature.{IpAddress, Measurement, SensorReading, Temperature, UnexpectedError, jsonEncoder} import cats.data.Kleisli import cats.effect.IO import org.http4s.Method.PUT import org.http4s.client.{DisposableResponse, Client => Http4sClient} import org.http4s.dsl.io._ import org.http4s.{EntityDecoder, Request} import org.specs2.matcher.DisjunctionMatchers._ import org.specs2.mutable.Specification class HttpUploadTest extends Specification { "Ip address pre-check" >> { IpAddress.currentIpAddress.size must be_>(0) } "Encode a measurement for the wire" >> { def encodeMessageViaEntityEncoder(measurement: Measurement): String = { implicit val encoder = jsonEncoder[Measurement] val request: IO[Request[IO]] = Request(PUT).withBody(measurement) EntityDecoder.decodeString(request.unsafeRunSync()).unsafeRunSync() } val measurement = Measurement(Host("example"), Seconds(1509221361), List(SensorReading("28-0115910f5eff", Temperature(19.75)))) encodeMessageViaEntityEncoder(measurement) must_== """|{ | "host" : { | "name" : "example", | "utcOffset" : null, | "timezone" : null | }, | "seconds" : 1509221361, | "sensors" : [ | { | "name" : "28-0115910f5eff", | "temperature" : { | "celsius" : 19.75 | } | } | ] |}""".stripMargin } "Error response from server" >> { val measurement = Measurement(Host("example"), Seconds(1509221361), List(SensorReading("28-0115910f5eff", Temperature(19.75)))) val error = InternalServerError("I'm an error").map(DisposableResponse(_, IO.pure(()))) val willError: Kleisli[IO, Request[IO], DisposableResponse[IO]] = new Kleisli[IO, Request[IO], DisposableResponse[IO]](_ => error) val client = Http4sClient[IO](willError, IO.pure(())) val upload = HttpUpload(InetAddress.getLoopbackAddress, client) val value = upload.write(measurement) value must be_-\/.like { case UnexpectedError("""Failed to PUT temperature data to http://127.0.0.1:11900/temperature, response was 500 Internal Server Error: Right(I'm an error)""") => ok } } "Request has headers" >> { val measurement = Measurement(Host("example"), Seconds(1509221361), List(SensorReading("28-0115910f5eff", Temperature(19.75)))) var headers = List[String]() val client = Http4sClient[IO](new Kleisli[IO, Request[IO], DisposableResponse[IO]](request => { headers = request.headers.map(_.name.toString()).toList Ok().map(DisposableResponse(_, IO.pure(()))) }), IO.pure(())) val upload = HttpUpload(InetAddress.getLoopbackAddress, client) upload.write(measurement) headers must_== List( "Content-Type", "X-Forwarded-For", "Content-Length" ) } }
Example 45
Source File: ExportEndpointTest.scala From temperature-machine with Apache License 2.0 | 5 votes |
package bad.robot.temperature.server import java.time.ZoneId import java.time.format.DateTimeFormatter import java.time.format.FormatStyle.SHORT import java.util.Locale._ import cats.effect.IO import org.http4s.Method.GET import org.http4s.Status.Ok import org.http4s.implicits._ import org.http4s.{Request, Uri} import org.specs2.mutable.Specification import scalaz.syntax.either._ class ExportEndpointTest extends Specification { sequential "convert json to csv" >> { val exampleJson = """ |[ | { | "label": "bedroom1-sensor-1", | "data": [ | { | "x": 1507709610000, | "y": "NaN" | }, | { | "x": 1507709640000, | "y": "+2.2062500000E01" | }, | { | "x": 1507709680000, | "y": "+2.2262500000E01" | } | ] | } |] """.stripMargin val expectedCsv = """"Sensor","Time","Temperature","Difference" |"bedroom1-sensor-1","11/10/17 08:13","NaN","0" |"bedroom1-sensor-1","11/10/17 08:14","22.0625","NaN" |"bedroom1-sensor-1","11/10/17 08:14","22.2625","0.20"""".stripMargin val UkDateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(SHORT).withLocale(UK).withZone(ZoneId.of("GMT")) val request = Request[IO](GET, Uri.uri("/temperatures.csv")) val service = ExportEndpoint(exampleJson.right, UkDateTimeFormatter) val response = service.orNotFound.run(request).unsafeRunSync() response.as[String].unsafeRunSync must_== expectedCsv response.status must_== Ok } }
Example 46
Source File: ServerSpec.scala From Learn-Scala-Programming with MIT License | 5 votes |
import ch14.{Config, Server} import cats.effect.IO import cats.implicits._ import io.circe.Json import io.circe.literal._ import org.http4s.circe._ import org.http4s.client.blaze.Http1Client import org.http4s.{Method, Request, Status, Uri} import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpec} import org.http4s.server.{Server => Http4sServer} class ServerSpec extends WordSpec with Matchers with BeforeAndAfterAll { private lazy val client = Http1Client[IO]().unsafeRunSync() private lazy val configIO = Config.load("test.conf") private lazy val config = configIO.unsafeRunSync() private lazy val rootUrl = s"http://${config.server.host}:${config.server.port}" private val server: Option[Http4sServer[IO]] = (for { builder <- Server.createServer(configIO) } yield builder.start.unsafeRunSync()).compile.last.unsafeRunSync() override def afterAll(): Unit = { client.shutdown.unsafeRunSync() server.foreach(_.shutdown.unsafeRunSync()) } "The server" should { "get an empty inventory" in { val json = client.expect[Json](s"$rootUrl/inventory").unsafeRunSync() json shouldBe json"""{}""" } "create articles" in { val eggs = Request[IO](method = Method.POST, uri = Uri.unsafeFromString(s"$rootUrl/articles/eggs")) client.status(eggs).unsafeRunSync() shouldBe Status.NoContent val chocolate = Request[IO](method = Method.POST, uri = Uri.unsafeFromString(s"$rootUrl/articles/chocolate")) client.status(chocolate).unsafeRunSync() shouldBe Status.NoContent val json = client.expect[Json](s"$rootUrl/inventory").unsafeRunSync() json shouldBe json"""{"eggs" : 0,"chocolate" : 0}""" } "update inventory" in { val restock = Request[IO](method = Method.POST, uri = Uri.unsafeFromString(s"$rootUrl/restock")).withBody(json"""{ "inventory" : { "eggs": 10, "chocolate": 20 }}""") client.expect[Json](restock).unsafeRunSync() shouldBe json"""{ "eggs" : 10, "chocolate" : 20 }""" client.expect[Json](restock).unsafeRunSync() shouldBe json"""{ "eggs" : 20, "chocolate" : 40 }""" } "deliver purchase if there is enough inventory" in { val purchase = Request[IO](method = Method.POST, uri = Uri.unsafeFromString(s"$rootUrl/purchase")).withBody(json"""{ "order" : { "eggs": 5, "chocolate": 5 }}""") client.expect[Json](purchase).unsafeRunSync() shouldBe json"""{ "eggs" : 5, "chocolate" : 5 }""" } "not deliver purchase if there is not enough inventory" in { val purchase = Request[IO](method = Method.POST, uri = Uri.unsafeFromString(s"$rootUrl/purchase")).withBody(json"""{ "order" : { "eggs": 5, "chocolate": 45 }}""") client.expect[Json](purchase).unsafeRunSync() shouldBe json"""{ "eggs" : 0, "chocolate" : 0 }""" } } }
Example 47
Source File: Http4sGitHubApiAlg.scala From scala-steward with Apache License 2.0 | 5 votes |
package org.scalasteward.core.github.http4s import org.http4s.{Request, Uri} import org.scalasteward.core.git.Branch import org.scalasteward.core.github._ import org.scalasteward.core.util.HttpJsonClient import org.scalasteward.core.vcs.VCSApiAlg import org.scalasteward.core.vcs.data._ final class Http4sGitHubApiAlg[F[_]]( gitHubApiHost: Uri, modify: Repo => Request[F] => F[Request[F]] )(implicit client: HttpJsonClient[F] ) extends VCSApiAlg[F] { private val url = new Url(gitHubApiHost) override def createFork(repo: Repo): F[RepoOut] = client.post(url.forks(repo), modify(repo)) override def createPullRequest(repo: Repo, data: NewPullRequestData): F[PullRequestOut] = client.postWithBody(url.pulls(repo), data, modify(repo)) override def getBranch(repo: Repo, branch: Branch): F[BranchOut] = client.get(url.branches(repo, branch), modify(repo)) override def getRepo(repo: Repo): F[RepoOut] = client.get(url.repos(repo), modify(repo)) override def listPullRequests(repo: Repo, head: String, base: Branch): F[List[PullRequestOut]] = client.get(url.listPullRequests(repo, head, base), modify(repo)) }
Example 48
Source File: Http4sBitbucketApiAlg.scala From scala-steward with Apache License 2.0 | 5 votes |
package org.scalasteward.core.bitbucket.http4s import cats.effect.Sync import cats.implicits._ import org.http4s.{Request, Status, Uri} import org.scalasteward.core.bitbucket.Url import org.scalasteward.core.bitbucket.http4s.json._ import org.scalasteward.core.git.Branch import org.scalasteward.core.util.{HttpJsonClient, UnexpectedResponse} import org.scalasteward.core.vcs.VCSApiAlg import org.scalasteward.core.vcs.data._ class Http4sBitbucketApiAlg[F[_]: Sync]( bitbucketApiHost: Uri, user: AuthenticatedUser, modify: Repo => Request[F] => F[Request[F]], doNotFork: Boolean )(implicit client: HttpJsonClient[F]) extends VCSApiAlg[F] { private val url = new Url(bitbucketApiHost) override def createFork(repo: Repo): F[RepoOut] = for { fork <- client.post[RepositoryResponse](url.forks(repo), modify(repo)).recoverWith { case UnexpectedResponse(_, _, _, Status.BadRequest, _) => client.get(url.repo(repo.copy(owner = user.login)), modify(repo)) } maybeParent <- fork.parent .map(n => client.get[RepositoryResponse](url.repo(n), modify(n))) .sequence[F, RepositoryResponse] } yield mapToRepoOut(fork, maybeParent) private def mapToRepoOut( repo: RepositoryResponse, maybeParent: Option[RepositoryResponse] ): RepoOut = RepoOut( repo.name, repo.owner, maybeParent.map(p => mapToRepoOut(p, None)), repo.httpsCloneUrl, repo.mainBranch ) override def createPullRequest(repo: Repo, data: NewPullRequestData): F[PullRequestOut] = { val sourceBranchOwner = if (doNotFork) repo.owner else user.login val payload = CreatePullRequestRequest( data.title, Branch(data.head), Repo(sourceBranchOwner, repo.repo), data.base, data.body ) client.postWithBody(url.pullRequests(repo), payload, modify(repo)) } override def getBranch(repo: Repo, branch: Branch): F[BranchOut] = client.get(url.branch(repo, branch), modify(repo)) override def getRepo(repo: Repo): F[RepoOut] = for { repo <- client.get[RepositoryResponse](url.repo(repo), modify(repo)) maybeParent <- repo.parent .map(n => client.get[RepositoryResponse](url.repo(n), modify(n))) .sequence[F, RepositoryResponse] } yield mapToRepoOut(repo, maybeParent) override def listPullRequests(repo: Repo, head: String, base: Branch): F[List[PullRequestOut]] = client .get[Page[PullRequestOut]](url.listPullRequests(repo, head), modify(repo)) .map(_.values) }
Example 49
Source File: HttpExistenceClient.scala From scala-steward with Apache License 2.0 | 5 votes |
package org.scalasteward.core.util import cats.effect.{Async, Resource} import cats.implicits._ import com.github.benmanes.caffeine.cache.Caffeine import io.chrisdavenport.log4cats.Logger import org.http4s.client.Client import org.http4s.{Method, Request, Status, Uri} import org.scalasteward.core.application.Config import scalacache.CatsEffect.modes._ import scalacache.caffeine.CaffeineCache import scalacache.{Async => _, _} final class HttpExistenceClient[F[_]](statusCache: Cache[Status])(implicit client: Client[F], logger: Logger[F], mode: Mode[F], F: MonadThrowable[F] ) { def exists(uri: Uri): F[Boolean] = status(uri).map(_ === Status.Ok).handleErrorWith { throwable => logger.debug(throwable)(s"Failed to check if $uri exists").as(false) } private def status(uri: Uri): F[Status] = statusCache.cachingForMemoizeF(uri.renderString)(None) { client.status(Request[F](method = Method.HEAD, uri = uri)) } } object HttpExistenceClient { def create[F[_]](implicit config: Config, client: Client[F], logger: Logger[F], F: Async[F] ): Resource[F, HttpExistenceClient[F]] = { val buildCache = F.delay { CaffeineCache( Caffeine .newBuilder() .maximumSize(16384L) .expireAfterWrite(config.cacheTtl.length, config.cacheTtl.unit) .build[String, Entry[Status]]() ) } Resource.make(buildCache)(_.close().void).map(new HttpExistenceClient[F](_)) } }
Example 50
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 51
Source File: JoexClient.scala From docspell with GNU General Public License v3.0 | 5 votes |
package docspell.joexapi.client import scala.concurrent.ExecutionContext import cats.effect._ import cats.implicits._ import docspell.common.syntax.all._ import docspell.common.{Ident, LenientUri} import docspell.joexapi.model.BasicResult import org.http4s.circe.CirceEntityDecoder._ import org.http4s.client.Client import org.http4s.client.blaze.BlazeClientBuilder import org.http4s.{Method, Request, Uri} import org.log4s.getLogger trait JoexClient[F[_]] { def notifyJoex(base: LenientUri): F[Unit] def notifyJoexIgnoreErrors(base: LenientUri): F[Unit] def cancelJob(base: LenientUri, job: Ident): F[BasicResult] } object JoexClient { private[this] val logger = getLogger def apply[F[_]: Sync](client: Client[F]): JoexClient[F] = new JoexClient[F] { def notifyJoex(base: LenientUri): F[Unit] = { val notifyUrl = base / "api" / "v1" / "notify" val req = Request[F](Method.POST, uri(notifyUrl)) logger.fdebug(s"Notify joex at ${notifyUrl.asString}") *> client.expect[String](req).map(_ => ()) } def notifyJoexIgnoreErrors(base: LenientUri): F[Unit] = notifyJoex(base).attempt.map { case Right(()) => () case Left(ex) => logger.warn( s"Notifying Joex instance '${base.asString}' failed: ${ex.getMessage}" ) () } def cancelJob(base: LenientUri, job: Ident): F[BasicResult] = { val cancelUrl = base / "api" / "v1" / "job" / job.id / "cancel" val req = Request[F](Method.POST, uri(cancelUrl)) client.expect[BasicResult](req) } private def uri(u: LenientUri): Uri = Uri.unsafeFromString(u.asString) } def resource[F[_]: ConcurrentEffect](ec: ExecutionContext): Resource[F, JoexClient[F]] = BlazeClientBuilder[F](ec).resource.map(apply[F]) }
Example 52
Source File: CollectionsServiceSpec.scala From franklin with Apache License 2.0 | 5 votes |
package com.azavea.franklin.api.services import cats.data.OptionT import cats.effect.IO import cats.implicits._ import com.azavea.franklin.Generators import com.azavea.franklin.api.{TestClient, TestServices} import com.azavea.franklin.database.TestDatabaseSpec import com.azavea.franklin.datamodel.CollectionsResponse import com.azavea.stac4s.StacCollection import com.azavea.stac4s.testing._ import org.http4s.circe.CirceEntityDecoder._ import org.http4s.{Method, Request, Uri} import org.specs2.{ScalaCheck, Specification} import java.net.URLEncoder import java.nio.charset.StandardCharsets class CollectionsServiceSpec extends Specification with ScalaCheck with TestDatabaseSpec with Generators { def is = s2""" This specification verifies that the collections service can run without crashing The collections service should: - create and delete collections $createDeleteCollectionExpectation - list collections $listCollectionsExpectation - get collections by id $getCollectionsExpectation """ val testServices: TestServices[IO] = new TestServices[IO](transactor) val testClient: TestClient[IO] = new TestClient[IO](testServices.collectionsService, testServices.collectionItemsService) def listCollectionsExpectation = prop { (stacCollectionA: StacCollection, stacCollectionB: StacCollection) => { val listIO = ( testClient.getCollectionResource(stacCollectionA), testClient.getCollectionResource(stacCollectionB) ).tupled use { _ => val request = Request[IO](method = Method.GET, Uri.unsafeFromString(s"/collections")) (for { resp <- testServices.collectionsService.routes.run(request) decoded <- OptionT.liftF { resp.as[CollectionsResponse] } } yield decoded).value } val result = listIO.unsafeRunSync.get.collections map { _.id } (result must contain(stacCollectionA.id)) and (result must contain(stacCollectionB.id)) } } def getCollectionsExpectation = prop { (stacCollection: StacCollection) => val fetchIO = testClient.getCollectionResource(stacCollection) use { collection => val encodedId = URLEncoder.encode(collection.id, StandardCharsets.UTF_8.toString) val request = Request[IO](method = Method.GET, Uri.unsafeFromString(s"/collections/$encodedId")) (for { resp <- testServices.collectionsService.routes.run(request) decoded <- OptionT.liftF { resp.as[StacCollection] } } yield (decoded, collection)).value } val (fetched, inserted) = fetchIO.unsafeRunSync.get fetched must beTypedEqualTo(inserted) } // since creation / deletion is a part of the collection resource, and accurate creation is checked // in getCollectionsExpectation, this test just makes sure that if other tests are failing, it's // not because create/delete are broken def createDeleteCollectionExpectation = prop { (stacCollection: StacCollection) => (testClient .getCollectionResource(stacCollection) use { _ => IO.unit }).unsafeRunSync must beTypedEqualTo( () ) } }
Example 53
Source File: TestClient.scala From franklin with Apache License 2.0 | 5 votes |
package com.azavea.franklin.api import cats.effect.Resource import cats.effect.Sync import cats.implicits._ import com.azavea.franklin.api.services.{CollectionItemsService, CollectionsService} import com.azavea.stac4s.{StacCollection, StacItem} import eu.timepit.refined.auto._ import io.circe.syntax._ import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ import org.http4s.implicits._ import org.http4s.{Method, Request, Uri} import java.net.URLEncoder import java.nio.charset.StandardCharsets class TestClient[F[_]: Sync]( collectionsService: CollectionsService[F], collectionItemsService: CollectionItemsService[F] ) { private def createCollection(collection: StacCollection): F[StacCollection] = collectionsService.routes.orNotFound.run( Request( method = Method.POST, uri = Uri.unsafeFromString("/collections") ).withEntity(collection.asJson) ) flatMap { _.as[StacCollection] } private def deleteCollection(collection: StacCollection): F[Unit] = { val encodedCollectionId = URLEncoder.encode(collection.id, StandardCharsets.UTF_8.toString) collectionsService.routes.orNotFound .run( Request( method = Method.DELETE, uri = Uri.unsafeFromString(s"/collections/$encodedCollectionId") ) ) .void } private def createItemInCollection(collection: StacCollection, item: StacItem): F[StacItem] = { val encodedCollectionId = URLEncoder.encode(collection.id, StandardCharsets.UTF_8.toString) collectionItemsService.routes.orNotFound.run( Request( method = Method.POST, uri = Uri.unsafeFromString(s"/collections/$encodedCollectionId/items") ).withEntity(item) ) flatMap { _.as[StacItem] } } private def deleteItemInCollection(collection: StacCollection, item: StacItem): F[Unit] = { val encodedCollectionId = URLEncoder.encode(collection.id, StandardCharsets.UTF_8.toString) val encodedItemId = URLEncoder.encode(item.id, StandardCharsets.UTF_8.toString) collectionItemsService.routes.orNotFound .run( Request( method = Method.DELETE, uri = Uri.unsafeFromString(s"/collections/$encodedCollectionId/items/$encodedItemId") ) ) .void } def getItemResource(collection: StacCollection, item: StacItem): Resource[F, StacItem] = Resource.make(createItemInCollection(collection, item.copy(collection = Some(collection.id))))( item => deleteItemInCollection(collection, item) ) def getCollectionResource(collection: StacCollection): Resource[F, StacCollection] = Resource.make(createCollection(collection))(collection => deleteCollection(collection)) def getCollectionItemResource( item: StacItem, collection: StacCollection ): Resource[F, (StacItem, StacCollection)] = (getItemResource(collection, item), getCollectionResource(collection)).tupled }
Example 54
Source File: Tracer.scala From http4s-tracer with Apache License 2.0 | 5 votes |
package dev.profunktor.tracer import cats.Applicative import cats.data.Kleisli import cats.effect.Sync import cats.syntax.all._ import org.http4s.syntax.StringSyntax import org.http4s.{Header, HttpApp, Request} object Tracer extends StringSyntax { private[tracer] val DefaultTraceIdHeader = "Trace-Id" final case class TraceId(value: String) extends AnyVal { override def toString = s"[Trace-Id] - [$value]" } def apply[F[_]](implicit ev: Tracer[F]): Tracer[F] = ev def create[F[_]](headerName: String = DefaultTraceIdHeader): Tracer[F] = new Tracer[F](headerName) } class Tracer[F[_]] private (headerName: String) { import Trace._, Tracer._ def middleware( http: HttpApp[F], logRequest: Boolean = false, logResponse: Boolean = false )(implicit F: Sync[F], L: TracerLog[Trace[F, ?]]): HttpApp[F] = Kleisli { req => val createId: F[(Request[F], TraceId)] = for { id <- GenUUID.make[F] tr <- F.delay(req.putHeaders(Header(headerName, id.value))) } yield (tr, id) for { mi <- getTraceId(req) (tr, id) <- mi.fold(createId)(id => (req, id).pure[F]) _ <- if (logRequest) L.info[Tracer[F]](s"$req").run(id) else F.unit rs <- http(tr).map(_.putHeaders(Header(headerName, id.value))) _ <- if (logResponse) L.info[Tracer[F]](s"$rs").run(id) else F.unit } yield rs } def loggingMiddleware( http: HttpApp[F] )(implicit F: Sync[F], L: TracerLog[Trace[F, ?]]): HttpApp[F] = middleware(http, logRequest = true, logResponse = true) def getTraceId(request: Request[F])(implicit F: Applicative[F]): F[Option[TraceId]] = F.pure(request.headers.get(headerName.ci).map(h => TraceId(h.value))) }
Example 55
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 } } } }
Example 56
Source File: Utils.scala From pizza-auth-3 with MIT License | 5 votes |
package moe.pizza.auth.webapp import moe.pizza.auth.interfaces.UserDatabase import moe.pizza.auth.models.Pilot import moe.pizza.auth.webapp.Types.{HydratedSession, Session2, Session} import moe.pizza.auth.webapp.Utils.Alerts.Alerts import org.http4s.{Uri, Response, Request} import org.http4s.dsl.{Root, _} import scalaz.concurrent.Task object Utils { object Alerts extends Enumeration { type Alerts = Value val success = Value("success") val info = Value("info") val warning = Value("warning") val danger = Value("danger") } implicit class PimpedSession2(s: Session2) { def hydrate(u: UserDatabase): HydratedSession = { new HydratedSession(s.alerts, s.redirect, s.uid.flatMap(u.getUser), s.signupData) } } implicit class PimpedHydratedSession(hs: HydratedSession) { def dehydrate(): Session2 = { new Session2(hs.alerts, hs.redirect, hs.pilot.map(_.uid), hs.signupData) } } implicit class PimpedRequest(r: Request) { def flash(level: Alerts, message: String): Option[HydratedSession] = { getSession.map(s => s.copy(alerts = s.alerts :+ Types.Alert(level.toString, message))) } def getSession = r.attributes.get(SessionManager.HYDRATEDSESSION) def setSession(s: Types.HydratedSession): Unit = r.attributes.put(SessionManager.HYDRATEDSESSION, s) def sessionResponse( f: ((HydratedSession, Pilot) => Task[Response]), error: String = "You must be signed in to do that"): Task[Response] = { (getSession, getSession.flatMap(_.pilot)) match { case (Some(s), Some(p)) => f(s, p) case _ => TemporaryRedirect(Uri(path = "/")) } } } implicit class PimpedResponse(r: Response) { def withSession(s: HydratedSession): Response = r.withAttribute(SessionManager.HYDRATEDSESSION, s) def getSession(): Option[HydratedSession] = r.attributes.get(SessionManager.HYDRATEDSESSION) def withNoSession(): Response = r.withAttribute(SessionManager.LOGOUT, "") } implicit class PimpedTaskResponse(r: Task[Response]) { def attachSessionifDefined(s: Option[HydratedSession]): Task[Response] = r.map(res => s.foldLeft(res) { (resp, sess) => resp.withSession(sess) }) def clearSession(): Task[Response] = r.map(res => res.withNoSession()) } def sanitizeUserName(name: String) = name.toLowerCase.replace("'", "").replace(" ", "_") }