cats.data.Kleisli Scala Examples

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

package natchez.http4s

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

object implicits {

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

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

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

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

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

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

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

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

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

} 
Example 2
Source File: ExampleApp.scala    From caliban   with Apache License 2.0 5 votes vote down vote up
package caliban.http4s

import caliban.ExampleData._
import caliban.ExampleService.ExampleService
import caliban.{ ExampleApi, ExampleService, Http4sAdapter }
import cats.data.Kleisli
import cats.effect.Blocker
import org.http4s.StaticFile
import org.http4s.implicits._
import org.http4s.server.Router
import org.http4s.server.blaze.BlazeServerBuilder
import org.http4s.server.middleware.CORS
import zio._
import zio.blocking.Blocking
import zio.interop.catz._

import scala.concurrent.ExecutionContext

object ExampleApp extends App {

  type ExampleTask[A] = RIO[ZEnv with ExampleService, A]

  override def run(args: List[String]): ZIO[ZEnv, Nothing, ExitCode] =
    ZIO
      .runtime[ZEnv with ExampleService]
      .flatMap(implicit runtime =>
        for {
          blocker     <- ZIO.access[Blocking](_.get.blockingExecutor.asEC).map(Blocker.liftExecutionContext)
          interpreter <- ExampleApi.api.interpreter
          _ <- BlazeServerBuilder[ExampleTask](ExecutionContext.global)
                .bindHttp(8088, "localhost")
                .withHttpApp(
                  Router[ExampleTask](
                    "/api/graphql" -> CORS(Http4sAdapter.makeHttpService(interpreter)),
                    "/ws/graphql"  -> CORS(Http4sAdapter.makeWebSocketService(interpreter)),
                    "/graphiql"    -> Kleisli.liftF(StaticFile.fromResource("/graphiql.html", blocker, None))
                  ).orNotFound
                )
                .resource
                .toManaged
                .useForever
        } yield ()
      )
      .provideCustomLayer(ExampleService.make(sampleCharacters))
      .exitCode
} 
Example 3
Source File: Issue121.scala    From guardrail   with MIT License 5 votes vote down vote up
package core.issues

import cats.effect.IO
import cats.data.Kleisli
import org.http4s._
import org.http4s.client.{ Client => Http4sClient }
import org.http4s.client.blaze._
import org.http4s.headers._
import org.http4s.implicits._
import org.http4s.multipart._
import cats.instances.future._
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.time.SpanSugar._
import org.scalatest.{ EitherValues, FunSuite, Matchers }
import io.circe._

class Issue121Suite extends FunSuite with Matchers with EitherValues with ScalaFutures {
  override implicit val patienceConfig = PatienceConfig(10 seconds, 1 second)

  test("http4s server can respond with 204") {
    import issues.issue121.server.http4s.{ DeleteFooResponse, Handler, Resource }

    val route = new Resource[IO]().routes(new Handler[IO] {
      override def deleteFoo(respond: DeleteFooResponse.type)(id: Long): IO[DeleteFooResponse] =
        IO.pure(respond.NoContent)
    })

    val client = Http4sClient.fromHttpApp[IO](route.orNotFound)

    val req = Request[IO](method = Method.DELETE, uri = Uri.unsafeFromString("/entity")).withEntity(UrlForm("id" -> "1234"))

    client
      .fetch(req)({
        case Status.NoContent(resp) =>
          IO.pure({
            resp.status should equal(Status.NoContent)
            resp.contentType should equal(None)
            resp.contentLength should equal(None)
            ()
          })
      })
      .unsafeRunSync()
  }

  test("http4s client can respond with 204") {
    import issues.issue121.client.http4s.Client

    def noContentResponse: Http4sClient[IO] =
      Http4sClient.fromHttpApp[IO](Kleisli.pure(Response[IO](Status.NoContent)))

    
    Client
      .httpClient(noContentResponse, "http://localhost:80")
      .deleteFoo(1234)
      .attempt
      .unsafeRunSync()
      .fold(
        _ => fail("Error"),
        _.fold(
          handleNoContent = ()
        )
      )
  }
} 
Example 4
Source File: Issue542.scala    From guardrail   with MIT License 5 votes vote down vote up
package core.issues

import cats.effect.IO
import cats.data.Kleisli
import org.http4s._
import org.http4s.circe._
import org.http4s.client.{ Client => Http4sClient }
import org.http4s.headers._
import org.http4s.implicits._
import cats.instances.future._
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.time.SpanSugar._
import org.scalatest.{ EitherValues, FunSuite, Matchers, OptionValues }
import tests.scalatest.EitherTValues

class Issue542Suite extends FunSuite with Matchers with EitherValues with ScalaFutures with EitherTValues with OptionValues {
  override implicit val patienceConfig = PatienceConfig(10 seconds, 1 second)

  test("base64 bytes can be sent") {
    import base64.server.http4s.{ FooResponse, Handler, Resource }
    import base64.server.http4s.definitions.Foo
    import base64.server.http4s.Implicits.Base64String

    val route = new Resource[IO]().routes(new Handler[IO] {
      def foo(respond: FooResponse.type)(): IO[FooResponse] = IO.pure(respond.Ok(Foo(Some(new Base64String("foo".getBytes())))))
    })

    val client = Http4sClient.fromHttpApp[IO](route.orNotFound)

    val req = Request[IO](method = Method.GET, uri = Uri.unsafeFromString("/foo"))

    client
      .fetch(req)({
        case Status.Ok(resp) =>
          resp.status should equal(Status.Ok)
          resp.contentType should equal(Some(`Content-Type`(MediaType.application.json)))
          resp.contentLength should equal(Some(16))
          jsonOf[IO, Foo].decode(resp, strict = false).rightValue
      })
      .unsafeRunSync()
      .value
      .value
      .data should equal("foo".getBytes())
  }

  test("base64 bytes can be received") {
    import base64.client.http4s.Client
    import base64.client.http4s.definitions.Foo
    import base64.client.http4s.Implicits.Base64String
    import org.http4s.dsl._

    def staticClient: Http4sClient[IO] = {
      implicit val fooOkEncoder = jsonEncoderOf[IO, Foo]
      val response = new Http4sDsl[IO] {
        def route: HttpApp[IO] = Kleisli.liftF(Ok(Foo(Some(Base64String("foo".getBytes())))))
      }
      Http4sClient.fromHttpApp[IO](response.route)
    }

    Client
      .httpClient(staticClient, "http://localhost:80")
      .foo()
      .attempt
      .unsafeRunSync()
      .fold(
        _ => fail("Error"),
        _.fold(
          handleOk = _.value.value.data should equal("foo".getBytes())
        )
      )
  }
} 
Example 5
Source File: CassandraOffsetStore.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.runtime.akkapersistence.readside

import java.util.UUID

import aecor.data.TagConsumer
import aecor.runtime.KeyValueStore
import aecor.util.effect._
import akka.persistence.cassandra.Session.Init
import akka.persistence.cassandra.session.scaladsl.CassandraSession
import cats.Functor
import cats.data.Kleisli
import cats.effect.Effect
import cats.implicits._

object CassandraOffsetStore {
  final case class Queries(keyspace: String, tableName: String = "consumer_offset") {
    def createTableQuery: String =
      s"CREATE TABLE IF NOT EXISTS $keyspace.$tableName (consumer_id text, tag text, offset uuid, PRIMARY KEY ((consumer_id, tag)))"
    def updateOffsetQuery: String =
      s"UPDATE $keyspace.$tableName SET offset = ? where consumer_id = ? AND tag = ?"
    def deleteOffsetQuery: String =
      s"DELETE FROM $keyspace.$tableName where consumer_id = ? AND tag = ?"
    def selectOffsetQuery: String =
      s"SELECT offset FROM $keyspace.$tableName WHERE consumer_id = ? AND tag = ?"
  }

  def apply[F[_]]: Builder[F] = builderInstance.asInstanceOf[Builder[F]]

  private val builderInstance = new Builder[Any]()

  final class Builder[F[_]] private[CassandraOffsetStore] () {
    def createTable(config: Queries)(implicit F: Functor[F]): Init[F] =
      Kleisli(_.execute(config.createTableQuery).void)

    def apply(session: CassandraSession, config: CassandraOffsetStore.Queries)(
      implicit F: Effect[F]
    ): CassandraOffsetStore[F] =
      new CassandraOffsetStore(session, config)
  }

}

class CassandraOffsetStore[F[_]] private[akkapersistence] (
  session: CassandraSession,
  config: CassandraOffsetStore.Queries
)(implicit F: Effect[F])
    extends KeyValueStore[F, TagConsumer, UUID] {

  private val selectOffsetStatement =
    session.prepare(config.selectOffsetQuery)

  private val updateOffsetStatement =
    session.prepare(config.updateOffsetQuery)

  private val deleteOffsetStatement =
    session.prepare(config.deleteOffsetQuery)

  override def setValue(key: TagConsumer, value: UUID): F[Unit] =
    F.fromFuture {
        updateOffsetStatement
      }
      .map { stmt =>
        stmt
          .bind()
          .setUUID("offset", value)
          .setString("tag", key.tag.value)
          .setString("consumer_id", key.consumerId.value)
      }
      .flatMap(x => F.fromFuture(session.executeWrite(x)))
      .void

  override def getValue(key: TagConsumer): F[Option[UUID]] =
    F.fromFuture {
        selectOffsetStatement
      }
      .map(_.bind(key.consumerId.value, key.tag.value))
      .flatMap(x => F.fromFuture(session.selectOne(x)))
      .map(_.map(_.getUUID("offset")))
  override def deleteValue(key: TagConsumer): F[Unit] =
    F.fromFuture {
        deleteOffsetStatement
      }
      .map(_.bind(key.consumerId.value, key.tag.value))
      .flatMap(x => F.fromFuture(session.executeWrite(x)))
      .void
} 
Example 6
Source File: Session.scala    From aecor   with MIT License 5 votes vote down vote up
package akka.persistence.cassandra
import java.util.concurrent.Executor

import cats.data.Kleisli
import cats.effect.{ Async, ContextShift }
import com.datastax.driver.core.{ ResultSet, TypeCodec, Session => DatastaxSession }

import scala.concurrent.ExecutionContext
import scala.util.control.NonFatal

trait Session[F[_]] {
  def execute(query: String): F[ResultSet]
  def registerCodec[A](codec: TypeCodec[A]): F[Unit]
}

object Session {
  type Init[F[_]] = Kleisli[F, Session[F], Unit]
  def Init[F[_]](f: Session[F] => F[Unit]): Init[F] = Kleisli(f)
  private val immediateExecutor = new Executor {
    override def execute(command: Runnable): Unit =
      command.run()
  }

  private val immediateExecutionContext = ExecutionContext.fromExecutor(immediateExecutor)

  def apply[F[_]](datastaxSession: DatastaxSession)(implicit F: Async[F],
                                                    contextShift: ContextShift[F]): Session[F] =
    new Session[F] {
      final override def execute(query: String): F[ResultSet] =
        contextShift.evalOn(immediateExecutionContext) {
          F.async { cb =>
            val future = datastaxSession.executeAsync(query)
            val runnable = new Runnable {
              override def run(): Unit =
                try {
                  cb(Right(future.get()))
                } catch {
                  case NonFatal(e) =>
                    cb(Left(e))
                }
            }
            future.addListener(runnable, immediateExecutor)
          }
        }
      override def registerCodec[A](codec: TypeCodec[A]): F[Unit] =
        F.delay {
          datastaxSession.getCluster.getConfiguration.getCodecRegistry.register(codec)
          ()
        }
    }
} 
Example 7
Source File: Minimal2.scala    From skunk   with MIT License 5 votes vote down vote up
// Copyright (c) 2018-2020 by Rob Norris
// This software is licensed under the MIT License (MIT).
// For more information see LICENSE or https://opensource.org/licenses/MIT

package example

import cats.Parallel
import cats.effect._
import cats.implicits._
import skunk._
import skunk.implicits._
import skunk.codec.all._
import natchez.Trace
import cats.data.Kleisli
import natchez.Span
import natchez.jaeger.Jaeger
import natchez.EntryPoint
import io.jaegertracing.Configuration.SamplerConfiguration
import io.jaegertracing.Configuration.ReporterConfiguration

object Minimal2 extends IOApp {

  def session[F[_]: Concurrent: ContextShift: Trace]: Resource[F, Session[F]] =
    Session.single(
      host     = "localhost",
      port     = 5432,
      user     = "jimmy",
      database = "world",
      password = Some("banana"),
      // debug    = true
    )

  case class Country(code: String, name: String, pop: Int)

  val select: Query[String, Country] =
    sql"""
      select code, name, population
      from country
      WHERE name like $varchar
    """.query(bpchar(3) ~ varchar ~ int4)
       .gmap[Country]

  def lookup[F[_]: Sync: Trace](pat: String, s: Session[F]): F[Unit] =
    Trace[F].span("lookup") {
      Trace[F].put("pattern" -> pat) *>
      s.prepare(select).use { pq =>
        pq.stream(pat, 1024)
          .evalMap(c => Sync[F].delay(println(s"⭐️⭐  $c")))
          .compile
          .drain
      }
    }

  def runF[F[_]: Concurrent: ContextShift: Trace: Parallel]: F[ExitCode] =
    session.use { s =>
      List("A%", "B%").parTraverse(p => lookup(p, s))
    } as ExitCode.Success

  def tracer[F[_]: Sync]: Resource[F, EntryPoint[F]] = {
    Jaeger.entryPoint[F]("skunk-http4s-example") { c =>
      Sync[F].delay {
        c.withSampler(SamplerConfiguration.fromEnv)
         .withReporter(ReporterConfiguration.fromEnv)
         .getTracer
      }
    }
  }

  def run(args: List[String]): IO[ExitCode] =
    tracer[IO].use { t =>
      t.root("root").use { s =>
        runF[Kleisli[IO, Span[IO], ?]].run(s) *>
        runF[Kleisli[IO, Span[IO], ?]].run(s)
      }
    }

} 
Example 8
Source File: LoginTest.scala    From scala-pet-store   with Apache License 2.0 5 votes vote down vote up
package io.github.pauljamescleary.petstore
package infrastructure.endpoint

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

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

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

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

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

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

  def signUpAndLogInAsCustomer(
      userSignUp: SignupRequest,
      userEndpoint: Kleisli[IO, Request[IO], Response[IO]],
  ): IO[(User, Option[Authorization])] =
    signUpAndLogIn(userSignUp.copy(role = Role.Customer), userEndpoint)
} 
Example 9
Source File: HttpMetricsMiddleware.scala    From core   with Apache License 2.0 5 votes vote down vote up
package com.smartbackpackerapp.http.metrics

import cats.data.{Kleisli, OptionT}
import cats.effect.Sync
import cats.syntax.flatMap._
import cats.syntax.functor._
import com.codahale.metrics._
import com.smartbackpackerapp.common.Log
import com.smartbackpackerapp.http.ApiVersion
import org.http4s.AuthedService
import org.http4s.Uri.Path

object HttpMetricsMiddleware {

  def apply[F[_]](registry: MetricRegistry,
                  service: AuthedService[String, F])
                 (implicit F: Sync[F], L: Log[F]): AuthedService[String, F] = {

    Kleisli { req =>
      OptionT.liftF(F.delay(System.nanoTime())).flatMap { start =>
        service(req).semiflatMap { response =>
          HttpMetrics.parse(req.req.uri.path).fold(F.delay(response)) { path =>
            for {
              _    <- F.delay(registry.meter(s"requests-$path").mark())
              _    <- if (response.status.isSuccess) F.delay(registry.meter(s"success-$path").mark())
                      else F.delay(registry.meter(s"failure-${response.status.code}-$path").mark())
              time <- F.delay((System.nanoTime() - start) / 1000000)
              _    <- F.delay(registry.histogram(s"response-time-$path").update(time))
              _    <- L.info(s"HTTP Response Time: $time ms")
            } yield response
          }
        }
      }
    }
  }

}

object HttpMetrics {

  def parse(path: Path): Option[String] = {
    if (path.contains("/traveling")) Some(s"$ApiVersion-traveling")
    else if (path.contains("/airlines")) Some(s"$ApiVersion-airlines")
    else if (path.contains("/ranking")) Some(s"$ApiVersion-ranking")
    else if (path.contains("/health")) Some(s"$ApiVersion-health")
    else if (path.contains("/countries")) Some(s"$ApiVersion-countries")
    else None
  }

} 
Example 10
Source File: JwtTokenAuthMiddleware.scala    From core   with Apache License 2.0 5 votes vote down vote up
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 11
Source File: Http4sUtils.scala    From core   with Apache License 2.0 5 votes vote down vote up
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 12
Source File: TaskMT.scala    From Waves   with MIT License 5 votes vote down vote up
package com.wavesplatform.lang.v1.task

import cats.data.Kleisli
import cats.implicits._
import cats.{Eval, Functor, Monad}
import com.wavesplatform.lang.EvalF
import monix.execution.atomic.{Atomic, AtomicBuilder}


trait TaskMT[F[_], S, E, R] {
  protected[task] val inner: Kleisli[Eval, EvalRef[S], F[Either[E, R]]]

  def run[RS <: Atomic[S]](initial: S)(implicit b: AtomicBuilder[S, RS]): Eval[(S, F[Either[E, R]])] = {
    val stateRef = EvalRef.of(initial)

    for {
      result     <- inner.run(stateRef)
      finalState <- stateRef.read
    } yield (finalState, result)
  }

  def map[B](f: R => B)(implicit ev: Functor[F]): TaskMT[F, S, E, B] =
    TaskMT.fromKleisli(inner.map(_.map {
      case Right(v)  => Right(f(v))
      case Left(err) => Left(err)
    }))

  def flatMap[B](f: R => TaskMT[F, S, E, B])(implicit m: Monad[EvalF[F, ?]]): TaskMT[F, S, E, B] = {
    TaskMT.fromEvalRef[F, S, E, B] { s =>
      m.flatMap(inner.run(s)) {
        case Right(v)  => f(v).inner.run(s)
        case Left(err) => m.pure(err.asLeft[B])
      }
    }
  }

  def handleErrorWith(f: E => TaskMT[F, S, E, R])(implicit m: Monad[EvalF[F, ?]]): TaskMT[F, S, E, R] =
    TaskMT.fromEvalRef[F, S, E, R] { s =>
      m.flatMap(inner.run(s)) {
        case Right(v)  => m.pure(v.asRight[E])
        case Left(err) => f(err).inner.run(s)
      }
    }

  def handleError()(implicit m: Monad[EvalF[F, ?]]): TaskMT[F, S, E, (Option[R], List[E])] = {
    TaskMT.fromEvalRef[F, S, E, (Option[R], List[E])] { s =>
      m.flatMap(inner.run(s)) {
        case Right(v)  => m.pure((Some(v), List.empty).asRight[E])
        case Left(err) => m.pure((None, List(err)).asRight[E])
      }
    }
  }
}

object TaskMT {
  private[task] def fromKleisli[F[_], S, E, R](in: Kleisli[Eval, EvalRef[S], F[Either[E, R]]]): TaskMT[F, S, E, R] =
    new TaskMT[F, S, E, R] {
      override protected[task] val inner: Kleisli[Eval, EvalRef[S], F[Either[E, R]]] = in
    }

  def apply[F[_], S, E, R](f: S => Eval[F[Either[E, R]]]): TaskMT[F, S, E, R] =
    fromEvalRef(_.read flatMap f)

  private def fromEvalRef[F[_], S, E, R](f: EvalRef[S] => Eval[F[Either[E, R]]]): TaskMT[F, S, E, R] =
    new TaskMT[F, S, E, R] {
      override protected[task] val inner: Kleisli[Eval, EvalRef[S], F[Either[E, R]]] = Kleisli(f)
    }
} 
Example 13
Source File: TaskMTFunctions.scala    From Waves   with MIT License 5 votes vote down vote up
package com.wavesplatform.lang.v1.task

import cats.{Eval, Monad}
import cats.data.Kleisli
import cats.implicits._
import com.wavesplatform.lang.EvalF

trait TaskMTFunctions {

  def pure[F[_] : Monad, S, E, R](x: R): TaskMT[F, S, E, R] =
    TaskMT(_ => Eval.now(x.asRight[E].pure[F]))

  def raiseError[F[_] : Monad, S, E, R](e: E): TaskMT[F, S, E, R] =
    TaskMT(_ => Eval.now(e.asLeft[R].pure[F]))

  def liftEither[F[_] : Monad, S, E, R](ei: Either[E, R]): TaskMT[F, S, E, R] =
    TaskMT.fromKleisli(Kleisli.pure(ei.pure[F]))

  def get[F[_] : Monad, S, E]: TaskMT[F, S, E, S] =
    TaskMT(s => Eval.now(s.asRight[E].pure[F]))

  def set[F[_] : Monad, S, E](s: S): TaskMT[F, S, E, Unit] =
    TaskMT.fromKleisli(Kleisli(ref => {
      ref.write(s).map(_.asRight[E].pure[F])
    }))

  def local[F[_], S, E, A](fa: TaskMT[F, S, E, A]): TaskMT[F, S, E, A] = {
    TaskMT.fromKleisli(Kleisli((ref: EvalRef[S]) => {
      val newRef = ref.copy()
      fa.inner.run(newRef)
    }))
  }

  def id[F[_], S, E, A](fa: TaskMT[F, S, E, A]): TaskMT[F, S, E, A] = fa

  def inspect[F[_] : Monad, S, E, A](f: S => A): TaskMT[F, S, E, A] =
    get[F, S, E].map(f)

  def inspectFlat[F[_] : Monad, S, E, A](f: S => TaskMT[F, S, E, A])(implicit m: Monad[EvalF[F, ?]]): TaskMT[F, S, E, A] =
    get[F, S, E].flatMap(f)

  def modify[F[_] : Monad, S, E](f: S => S)(implicit m: Monad[EvalF[F, ?]]): TaskMT[F, S, E, Unit] =
    get[F, S, E].flatMap(f andThen set[F, S, E])
} 
Example 14
Source File: ConditionalLoggerSpec.scala    From odin   with Apache License 2.0 5 votes vote down vote up
package io.odin.extras.loggers

import cats.data.Kleisli
import cats.effect.Sync
import cats.effect.concurrent.Ref
import cats.mtl.instances.all._
import cats.syntax.applicativeError._
import cats.syntax.flatMap._
import cats.syntax.order._
import io.odin.loggers.{DefaultLogger, HasContext}
import io.odin.syntax._
import io.odin.extras.syntax._
import io.odin.{Level, LoggerMessage, OdinSpec}
import monix.eval.Task
import monix.execution.schedulers.TestScheduler

class ConditionalLoggerSpec extends OdinSpec {

  implicit private val scheduler: TestScheduler = TestScheduler()

  type F[A] = Kleisli[Task, Map[String, String], A]

  case class RefLogger(ref: Ref[F, List[LoggerMessage]]) extends DefaultLogger[F] {
    def log(msg: LoggerMessage): F[Unit] = ref.update(_ :+ msg)
  }

  implicit private val hasContext: HasContext[Map[String, String]] = (env: Map[String, String]) => env

  it should "use log level of the inner logger in case of success" in {
    forAll { (messages: List[LoggerMessage], ctx: Map[String, String]) =>
      val fa =
        for {
          ref <- Ref.of[F, List[LoggerMessage]](List.empty)

          _ <- RefLogger(ref)
            .withMinimalLevel(Level.Info)
            .withContext
            .withErrorLevel(Level.Debug)(logger => logger.log(messages))

          written <- ref.get
        } yield written

      val written = fa.run(ctx).runSyncUnsafe()
      val expected = messages.filter(_.level >= Level.Info).map(m => m.copy(context = m.context ++ ctx))

      written shouldBe expected
    }
  }

  it should "use log level of the conditional logger in case of error" in {
    forAll { (messages: List[LoggerMessage], ctx: Map[String, String]) =>
      val error = new RuntimeException("Boom")

      val fa =
        for {
          ref <- Ref.of[F, List[LoggerMessage]](List.empty)

          attempt <- RefLogger(ref)
            .withMinimalLevel(Level.Info)
            .withContext
            .withErrorLevel(Level.Debug)(logger => logger.log(messages) >> Sync[F].raiseError[Unit](error))
            .attempt

          written <- ref.get
        } yield (attempt, written)

      val (attempt, written) = fa.run(ctx).runSyncUnsafe()
      val expected = messages.filter(_.level >= Level.Debug).map(m => m.copy(context = m.context ++ ctx))

      attempt shouldBe Left(error)
      written shouldBe expected
    }
  }

} 
Example 15
Source File: ExampleApp.scala    From caliban   with Apache License 2.0 5 votes vote down vote up
package caliban.tapir

import caliban.interop.tapir._
import caliban.tapir.Endpoints._
import caliban.{ GraphQL, Http4sAdapter }
import cats.data.Kleisli
import cats.effect.Blocker
import org.http4s.StaticFile
import org.http4s.implicits._
import org.http4s.server.Router
import org.http4s.server.blaze.BlazeServerBuilder
import org.http4s.server.middleware.CORS
import sttp.tapir.server.ServerEndpoint
import zio._
import zio.blocking.Blocking
import zio.interop.catz._
import zio.interop.catz.implicits._

import scala.concurrent.ExecutionContext

object ExampleApp extends CatsApp {

  // approach 1: using `Endpoint` and providing the logic
  val graphql: GraphQL[Any] =
    addBook.toGraphQL((bookAddLogic _).tupled) |+|
      deleteBook.toGraphQL((bookDeleteLogic _).tupled) |+|
      booksListing.toGraphQL((bookListingLogic _).tupled)

  // approach 2: using the `ServerEndpoint` where logic is already provided
  type MyIO[+A] = IO[String, A]

  val addBookEndpoint: ServerEndpoint[(Book, String), String, Unit, Nothing, MyIO] =
    addBook.serverLogic[MyIO] { case (book, token) => bookAddLogic(book, token).either }
  val deleteBookEndpoint: ServerEndpoint[(String, String), String, Unit, Nothing, MyIO] =
    deleteBook.serverLogic[MyIO] { case (title, token) => bookDeleteLogic(title, token).either }
  val booksListingEndpoint: ServerEndpoint[(Option[Int], Option[Int]), Nothing, List[Book], Nothing, UIO] =
    booksListing.serverLogic[UIO] { case (year, limit) => bookListingLogic(year, limit).map(Right(_)) }

  val graphql2: GraphQL[Any] =
    addBookEndpoint.toGraphQL |+| deleteBookEndpoint.toGraphQL |+| booksListingEndpoint.toGraphQL

  override def run(args: List[String]): ZIO[ZEnv, Nothing, ExitCode] =
    (for {
      blocker     <- ZIO.access[Blocking](_.get.blockingExecutor.asEC).map(Blocker.liftExecutionContext)
      interpreter <- graphql.interpreter
      _ <- BlazeServerBuilder[Task](ExecutionContext.global)
            .bindHttp(8088, "localhost")
            .withHttpApp(
              Router[Task](
                "/api/graphql" -> CORS(Http4sAdapter.makeHttpService(interpreter)),
                "/graphiql"    -> Kleisli.liftF(StaticFile.fromResource("/graphiql.html", blocker, None))
              ).orNotFound
            )
            .resource
            .toManaged
            .useForever
    } yield ()).exitCode
} 
Example 16
Source File: FederatedApp.scala    From caliban   with Apache License 2.0 5 votes vote down vote up
package caliban.federation

import caliban.Http4sAdapter
import caliban.federation.FederationData.characters.sampleCharacters
import caliban.federation.FederationData.episodes.sampleEpisodes
import cats.data.Kleisli
import cats.effect.Blocker
import org.http4s.StaticFile
import org.http4s.implicits._
import org.http4s.server.Router
import org.http4s.server.blaze.BlazeServerBuilder
import org.http4s.server.middleware.CORS
import zio._
import zio.blocking.Blocking
import zio.interop.catz._

import scala.concurrent.ExecutionContext

object FederatedApp extends CatsApp {
  type ExampleTask[A] = RIO[ZEnv, A]

  val service1 = CharacterService
    .make(sampleCharacters)
    .memoize
    .use(layer =>
      for {
        blocker     <- ZIO.access[Blocking](_.get.blockingExecutor.asEC).map(Blocker.liftExecutionContext)
        interpreter <- FederatedApi.Characters.api.interpreter.map(_.provideCustomLayer(layer))
        _ <- BlazeServerBuilder[ExampleTask](ExecutionContext.global)
              .bindHttp(8089, "localhost")
              .withHttpApp(
                Router[ExampleTask](
                  "/api/graphql" -> CORS(Http4sAdapter.makeHttpService(interpreter)),
                  "/graphiql"    -> Kleisli.liftF(StaticFile.fromResource("/graphiql.html", blocker, None))
                ).orNotFound
              )
              .resource
              .toManaged
              .useForever
      } yield ()
    )

  val service2 = EpisodeService
    .make(sampleEpisodes)
    .memoize
    .use(layer =>
      for {
        blocker     <- ZIO.access[Blocking](_.get.blockingExecutor.asEC).map(Blocker.liftExecutionContext)
        interpreter <- FederatedApi.Episodes.api.interpreter.map(_.provideCustomLayer(layer))
        _ <- BlazeServerBuilder[ExampleTask](ExecutionContext.global)
              .bindHttp(8088, "localhost")
              .withHttpApp(
                Router[ExampleTask](
                  "/api/graphql" -> CORS(Http4sAdapter.makeHttpService(interpreter)),
                  "/graphiql"    -> Kleisli.liftF(StaticFile.fromResource("/graphiql.html", blocker, None))
                ).orNotFound
              )
              .resource
              .toManaged
              .useForever
      } yield ()
    )

  override def run(args: List[String]): ZIO[ZEnv, Nothing, ExitCode] =
    (service1 race service2).exitCode
} 
Example 17
Source File: CartRoutesSpec.scala    From pfps-shopping-cart   with Apache License 2.0 5 votes vote down vote up
package shop.http.routes.secured

import cats.data.Kleisli
import cats.effect._
import java.util.UUID
import org.http4s._
import org.http4s.Method._
import org.http4s.client.dsl.io._
import org.http4s.server.AuthMiddleware
import shop.algebras.ShoppingCart
import shop.arbitraries._
import shop.domain.auth._
import shop.domain.cart._
import shop.domain.item._
import shop.http.auth.users._
import shop.http.json._
import squants.market.USD
import suite._

class CartRoutesSpec extends HttpTestSuite {

  val authUser = CommonUser(User(UserId(UUID.randomUUID), UserName("user")))

  val authMiddleware: AuthMiddleware[IO, CommonUser] =
    AuthMiddleware(Kleisli.pure(authUser))

  def dataCart(cartTotal: CartTotal) = new TestShoppingCart {
    override def get(userId: UserId): IO[CartTotal] =
      IO.pure(cartTotal)
  }

  test("GET shopping cart [OK]") {
    forAll { (ct: CartTotal) =>
      IOAssertion {
        GET(Uri.uri("/cart")).flatMap { req =>
          val routes = new CartRoutes[IO](dataCart(ct)).routes(authMiddleware)
          assertHttp(routes, req)(Status.Ok, ct)
        }
      }
    }
  }

  test("POST add item to shopping cart [OK]") {
    forAll { (c: Cart) =>
      IOAssertion {
        POST(c, Uri.uri("/cart")).flatMap { req =>
          val routes = new CartRoutes[IO](new TestShoppingCart).routes(authMiddleware)
          assertHttpStatus(routes, req)(Status.Created)
        }
      }
    }
  }

}

protected class TestShoppingCart extends ShoppingCart[IO] {
  def add(userId: UserId, itemId: ItemId, quantity: Quantity): IO[Unit] = IO.unit
  def get(userId: UserId): IO[CartTotal] =
    IO.pure(CartTotal(List.empty, USD(0)))
  def delete(userId: UserId): IO[Unit]                     = IO.unit
  def removeItem(userId: UserId, itemId: ItemId): IO[Unit] = IO.unit
  def update(userId: UserId, cart: Cart): IO[Unit]         = IO.unit
} 
Example 18
Source File: KleisliExamples.scala    From scala_typeclassopedia   with Creative Commons Attribution Share Alike 4.0 International 5 votes vote down vote up
package examples

import cats.data.Kleisli
import cats.implicits._


object KleisliExamples extends App {
  val twice: Int => Int = _ * 2
  val countCats: Int => String = x => if(x ==1) "1 cat" else s"$x cats"
  val twiceAsManyCats = twice andThen countCats
  println(twiceAsManyCats(1))

  val parse: String => Option[Int] = s => try { Some(s.toInt) } catch { case _: NumberFormatException => None }
  val reciprocal: Int => Option[Double] = i => if (i != 0) Some(1.0 / i) else None

  val parseKleisli = Kleisli(parse)
  val reciprocalKleisli = Kleisli(reciprocal)
  val parseAndReciprocal = reciprocalKleisli.compose(parseKleisli)
  val parseAndReciprocal2 = parseKleisli.andThen(reciprocalKleisli)

  println(parseAndReciprocal("10"))
  println(parseAndReciprocal2("10"))
} 
Example 19
Source File: Trees.scala    From github   with MIT License 5 votes vote down vote up
package io.chrisdavenport.github.endpoints.gitdata

import cats.implicits._
import cats.data.Kleisli
import cats.effect._
import org.http4s._
import org.http4s.client._
import org.http4s.implicits._

import io.chrisdavenport.github.Auth
import io.chrisdavenport.github.internals.GithubMedia._
import io.chrisdavenport.github.internals.RequestConstructor

import io.chrisdavenport.github.data.GitData._

object Trees {

  
  def createTree[F[_]: Sync](
    owner: String,
    repo: String,
    createTree: CreateTree,
    auth: Auth
  ): Kleisli[F, Client[F], Tree] = 
    RequestConstructor.runRequestWithBody[F, CreateTree, Tree](
      auth.some,
      Method.POST,
      uri"repos" / owner / repo / "git" / "trees",
      createTree
    )


} 
Example 20
Source File: Commits.scala    From github   with MIT License 5 votes vote down vote up
package io.chrisdavenport.github.endpoints.gitdata

import cats.implicits._
import cats.data.Kleisli
import cats.effect._
import org.http4s._
import org.http4s.client._
import org.http4s.implicits._

import io.chrisdavenport.github.Auth
import io.chrisdavenport.github.internals.GithubMedia._
import io.chrisdavenport.github.internals.RequestConstructor

import io.chrisdavenport.github.data.GitData._


  def createCommit[F[_]: Sync](
    owner: String,
    repo: String,
    createCommit: CreateCommit,
    auth: Auth
  ): Kleisli[F, Client[F], GitCommit] = 
  RequestConstructor.runRequestWithBody[F, CreateCommit, GitCommit](
    auth.some,
    Method.POST,
    uri"repos" / owner / repo / "git" / "commits",
    createCommit
  )




} 
Example 21
Source File: Blobs.scala    From github   with MIT License 5 votes vote down vote up
package io.chrisdavenport.github.endpoints.gitdata

import cats.implicits._
import cats.data.Kleisli
import cats.effect._
import org.http4s._
import org.http4s.client._
import org.http4s.implicits._

import io.chrisdavenport.github.Auth
import io.chrisdavenport.github.internals.GithubMedia._
import io.chrisdavenport.github.internals.RequestConstructor

import io.chrisdavenport.github.data.GitData._

object Blobs {

  
  def createBlob[F[_]: Sync](
    owner: String,
    repo: String,
    createBlob: CreateBlob,
    auth: Auth
  ): Kleisli[F, Client[F], NewBlob] = 
    RequestConstructor.runRequestWithBody[F, CreateBlob, NewBlob](
      auth.some,
      Method.POST,
      uri"repos" / owner / repo / "git" / "blobs",
      createBlob
    )


} 
Example 22
Source File: Tags.scala    From github   with MIT License 5 votes vote down vote up
package io.chrisdavenport.github.endpoints.gitdata

import cats.implicits._
import cats.data.Kleisli
import cats.effect._
import org.http4s._
import org.http4s.client._
import org.http4s.implicits._

import io.chrisdavenport.github.Auth
import io.chrisdavenport.github.internals.GithubMedia._
import io.chrisdavenport.github.internals.RequestConstructor

import io.chrisdavenport.github.data.GitData._

object Tags {
  
  def createTagFull[F[_]: Sync](
    owner: String,
    repo: String,
    createTag: CreateTag,
    auth: Auth
  ): Kleisli[F, Client[F], (GitTag, GitReference)] = 
    for {
      tag <- Tags.createTag[F](
        owner,
        repo,
        createTag, 
        auth
      )
      ref <- References.createReference[F](
        owner,
        repo,
        CreateReference(
          "refs/tags/" |+| createTag.tag,
          createTag.objectSha
        ),
        auth
      )
    } yield (tag, ref)

} 
Example 23
Source File: PaginatedJsonFiles.scala    From github   with MIT License 5 votes vote down vote up
package io.chrisdavenport.github.endpoints.utils

import cats.data.Kleisli
import cats.effect._
import io.circe.Json
import org.http4s._
import org.http4s.dsl.io._
import org.http4s.implicits._
import org.http4s.circe._

trait PaginatedJsonFiles extends JsonFiles with Paginate {

  def baseUri: Uri

  def pageFileName: Int => String

  def extractRequest: PartialFunction[Request[IO], Request[IO]]

  def paginatedEndpoint(numPages: Int): Kleisli[IO, Request[IO], Response[IO]] =
    HttpRoutes.of[IO] {
      extractRequest.andThen { request =>
        val page: Int = getCurrentPage(request)
        (for {
          jsonContent <- pages(numPages).get(page)
          linkHeader <- links(numPages).get(page)
        } yield Ok(jsonContent).map(_.putHeaders(linkHeader)))
          .getOrElse {
            NotFound(s"Page does not exist: $page")
          }
      }
    }.orNotFound

  private def pages(numPages: Int): Map[Int, Json] =
    getPageContents(numPages, pageFileName)

  private def links(numPages: Int): Map[Int, Header] =
    paginate(baseUri, numPages)

} 
Example 24
Source File: Http4sServerTests.scala    From tapir   with Apache License 2.0 5 votes vote down vote up
package sttp.tapir.server.http4s

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

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

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

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

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

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

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

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

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

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

      BlazeServerBuilder[IO](ExecutionContext.global)
        .bindHttp(port, "localhost")
        .withHttpApp(Router("/api" -> routes).orNotFound)
        .resource
        .use { _ => basicRequest.get(uri"http://localhost:$port/api/test/router").send().map(_.body shouldBe Right("ok")) }
        .unsafeRunSync()
    }
  }
} 
Example 25
Source File: CorrelationIdMiddleware.scala    From scala-server-toolkit   with MIT License 5 votes vote down vote up
package com.avast.sst.http4s.server.middleware

import java.util.UUID

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


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

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

  private val F = Sync[F]

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

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

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

object CorrelationIdMiddleware {

  final case class CorrelationId(value: String) extends AnyVal

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

} 
Example 26
Source File: Kleislis.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package freestyle.free.cache.redis.rediscala

import cats.data.Kleisli

import scala.concurrent.Future
import _root_.redis.{
  Cursor,
  ByteStringDeserializer => Deserializer,
  ByteStringSerializer => Serializer
}
import _root_.redis.commands.{
  Keys => KeyCommands,
  Server => ServerCommands,
  Strings => StringCommands
}

private[rediscala] trait StringCommandsCont {

  def append[Key, Value](key: Key, value: Value)(
      implicit format: Format[Key],
      writer: Serializer[Value]
  ): Ops[Future, Long] =
    Kleisli((client: StringCommands) => client.append(format(key), value))

  def get[Key, Value](key: Key)(
      implicit format: Format[Key],
      writer: Deserializer[Value]
  ): Ops[Future, Option[Value]] =
    Kleisli((client: StringCommands) => client.get[Value](format(key)))

  def set[Key: Format, Value: Serializer](key: Key, value: Value): Ops[Future, Boolean] =
    Kleisli((client: StringCommands) => client.set(key, value))

  def mset[Key, Value: Serializer](keyValues: Map[Key, Value])(
      implicit format: Format[Key]): Ops[Future, Boolean] = {
    val b = keyValues.map { case (k, v) => (format(k), v) }
    Kleisli((client: StringCommands) => client.mset(b))
  }

  def setnx[Key: Format, Value: Serializer](key: Key, value: Value): Ops[Future, Boolean] =
    Kleisli((client: StringCommands) => client.setnx(key, value))

  def setxx[Key: Format, Value: Serializer](key: Key, value: Value): Ops[Future, Boolean] =
    Kleisli((client: StringCommands) => client.set(key, value, XX = true))

}

private[rediscala] trait KeyCommandsCont {

  def del[Key](keys: List[Key])(implicit format: Format[Key]): Ops[Future, Long] =
    Kleisli((client: KeyCommands) => client.del(keys.map(format): _*))

  def exists[Key](key: Key)(implicit format: Format[Key]): Ops[Future, Boolean] =
    Kleisli((client: KeyCommands) => client.exists(format(key)))

  def keys[Key]: Ops[Future, Seq[String]] =
    Kleisli((client: KeyCommands) => client.keys("*"))

  def scan[Key]: Ops[Future, Cursor[Seq[String]]] =
    Kleisli((client: KeyCommands) => client.scan(0, Option(1), None))
}

private[rediscala] trait ServerCommandsCont {

  def flushDB: Ops[Future, Boolean] =
    Kleisli((client: ServerCommands) => client.flushdb)

}

private[rediscala] object RediscalaCont
    extends StringCommandsCont
    with KeyCommandsCont
    with ServerCommandsCont 
Example 27
Source File: PeerSelector.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.core.peer

import cats.data.Kleisli
import cats.effect.Sync
import cats.implicits._
import jbok.common.math.N
import jbok.core.messages.SignedTransactions
import jbok.core.models.Block

import scala.util.Random

object PeerSelector {
  type PeerSelector[F[_]] = Kleisli[F, List[Peer[F]], List[Peer[F]]]

  def apply[F[_]](run: List[Peer[F]] => F[List[Peer[F]]]): PeerSelector[F] =
    Kleisli(run)

  def all[F[_]: Sync]: PeerSelector[F] = PeerSelector(Sync[F].pure)

  def except[F[_]: Sync](peer: Peer[F]): PeerSelector[F] = PeerSelector(_.filterNot(_ == peer).pure[F])

  def one[F[_]: Sync](peer: Peer[F]): PeerSelector[F] = PeerSelector(_ => Sync[F].pure(List(peer)))

  def many[F[_]: Sync](list: List[Peer[F]]): PeerSelector[F] = PeerSelector(_ => Sync[F].pure(list))

  def withoutBlock[F[_]: Sync](block: Block): PeerSelector[F] = PeerSelector { peers =>
    peers
      .traverse[F, Option[Peer[F]]] { peer =>
        peer.hasBlock(block.header.hash).map {
          case true  => None
          case false => Some(peer)
        }
      }
      .map(_.flatten)
  }

  def withoutTxs[F[_]: Sync](stxs: SignedTransactions): PeerSelector[F] = PeerSelector { peers =>
    peers
      .traverse[F, Option[Peer[F]]] { peer =>
        peer.hasTxs(stxs).map {
          case true  => None
          case false => Some(peer)
        }
      }
      .map(_.flatten)
  }

  def bestPeer[F[_]: Sync](minTD: N): PeerSelector[F] = PeerSelector { peers =>
    peers
      .traverse(p => p.status.get.map(_.td).map(td => td -> p))
      .map(_.filter(_._1 > minTD).sortBy(-_._1).map(_._2))
  }

  def randomSelectSqrt[F[_]: Sync](min: Int): PeerSelector[F] = PeerSelector { peers =>
    val numberOfPeersToSend = math.max(math.sqrt(peers.size).toInt, min)
    Sync[F].pure(Random.shuffle(peers).take(numberOfPeersToSend))
  }
} 
Example 28
Source File: Server.scala    From zio-metrics   with Apache License 2.0 5 votes vote down vote up
package zio.metrics.dropwizard

import scala.util.Properties.envOrNone

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

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

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

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

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

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

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

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

import cats.{ Monad, Eval }
import cats.data.Kleisli
import cats.implicits._

final object ModelDecoding {

  sealed abstract class Proc[F[_], S] extends Monad[F] {
    def get: F[S]
    def set(s: S): F[Unit]
    def raise[A](err: String): F[A]
    def force[A](proc: F[A], s: S): Either[String, A]
    def forceAS[A](proc: F[A], s: S): (S, Either[String, A])
  }

  sealed abstract class Api {
    type F[S, A]
    implicit def procInstance[S]: Proc[F[S, ?], S]
  }

  val Api: Api = Impl

  type Error = String

  final case class DecodingError(err: Error)
    extends Exception(sh"error while decoding model: ${err}")

  private final object Impl extends Api {
    override type F[S, A] = Kleisli[Eval, LocRef[S], A]
    implicit override def procInstance[S]: Proc[F[S, ?], S] = {
      new Proc[F[S, ?], S] {
        override def pure[A](x: A) =
          Kleisli.pure(x)
        override def flatMap[A, B](fa: Kleisli[Eval, LocRef[S], A])(
          f: A => Kleisli[Eval, LocRef[S], B]
        ) = fa.flatMap(f)
        override def tailRecM[A, B](a: A)(f: A => F[S, Either[A, B]]): F[S, B] =
          Kleisli.catsDataMonadForKleisli[Eval, LocRef[S]].tailRecM(a)(f)
        override def raise[A](err: String) =
          Kleisli { _ => Eval.always(throw new DecodingError(err)) }
        override def get =
          Kleisli { ref => ref.get }
        override def set(s: S) =
          Kleisli { ref => ref.set(s) }
        override def force[A](proc: F[S, A], s: S): Either[String, A] = {
          try Right(proc.run(new LocRef[S](s)).value)
          catch { case DecodingError(err) => Left(err) }
        }
        override def forceAS[A](proc: F[S, A], s: S): (S, Either[String, A]) = {
          val ref = new LocRef[S](s)
          val res = try {
            Right(proc.run(ref).value)
          } catch { case DecodingError(err) => Left(err) }
          (ref.get.value, res)
        }
      }
    }
  }

  private final class LocRef[A](private[this] var value: A) {
    def get: Eval[A] = Eval.always(this.value)
    def set(a: A): Eval[Unit] = Eval.always(this.value = a)
  }
} 
Example 30
Source File: SecuredRequestHandler.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authentication

import cats.ApplicativeError
import cats.MonadError
import cats.data.{Kleisli, OptionT}
import cats.syntax.all._
import org.http4s._
import org.log4s._
import tsec.authorization._

sealed abstract class SecuredRequestHandler[F[_], Identity, User, Auth](
    val authenticator: Authenticator[F, Identity, User, Auth]
)(implicit F: MonadError[F, Throwable], ME: MonadError[Kleisli[OptionT[F, ?], Request[F], ?], Throwable]) {

  private[this] val cachedUnauthorized: Response[F]                       = Response[F](Status.Unauthorized)
  private[this] val defaultNotAuthenticated: Request[F] => F[Response[F]] = _ => F.pure(cachedUnauthorized)

  
  private[tsec] def default[F[_], Identity, User, Auth](
      authenticator: Authenticator[F, Identity, User, Auth]
  )(implicit F: MonadError[F, Throwable]): SecuredRequestHandler[F, Identity, User, Auth] =
    new SecuredRequestHandler[F, Identity, User, Auth](authenticator) {}

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

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

object KamonSupport {

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

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


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

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

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


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

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

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

import cats.data.Kleisli
import cats.effect.IO
import org.http4s.CacheDirective.{`max-age`, `public`}
import org.http4s.HttpService
import org.http4s.Status.Successful
import org.http4s.headers.`Cache-Control`
import org.http4s.server.staticcontent._

import scala.concurrent.duration._

object StaticResources {
  def apply(): HttpService[IO] = Kleisli.apply(request => {
    val resources = resourceService[IO](ResourceService.Config(basePath = ""))

    val response = if (request.uri.path.endsWith("/"))
      resources(request.withUri(request.uri.withPath(request.uri.path + "index.html")))
    else
      resources(request)

    response.map {
      case Successful(resp) => resp.putHeaders(`Cache-Control`(`public`, `max-age`(Duration(365, DAYS))))
      case resp             => resp
    }
  })

} 
Example 33
Source File: HttpUploadTest.scala    From temperature-machine   with Apache License 2.0 5 votes vote down vote up
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 34
Source File: RestTemplateInterpreter.scala    From hammock   with MIT License 5 votes vote down vote up
package hammock
package resttemplate

import java.net.URI

import cats._
import cats.implicits._
import cats.data.Kleisli
import cats.effect._
import org.springframework.http._
import org.springframework.util.LinkedMultiValueMap
import org.springframework.web.client.RestTemplate

import scala.collection.JavaConverters._

object RestTemplateInterpreter {

  def apply[F[_]](implicit F: InterpTrans[F]): InterpTrans[F] = F

  implicit def instance[F[_]: Sync](
      implicit client: RestTemplate = new RestTemplate()
  ): InterpTrans[F] = new InterpTrans[F] {
    override def trans: HttpF ~> F = transK andThen λ[Kleisli[F, RestTemplate, *] ~> F](_.run(client))
  }

  def transK[F[_]: Sync]: HttpF ~> Kleisli[F, RestTemplate, *] = {
    λ[HttpF ~> Kleisli[F, RestTemplate, *]] {
      case reqF @ (Get(_) | Delete(_) | Head(_) | Options(_) | Trace(_) | Post(_) | Put(_) | Patch(_)) =>
        Kleisli { implicit client =>
          for {
            req             <- mapRequest[F](reqF)
            res             <- execute[F](req)
            hammockResponse <- mapResponse[F](res)
          } yield hammockResponse
        }
    }
  }

  def mapRequest[F[_]: Sync](reqF: HttpF[HttpResponse]): F[RequestEntity[String]] = {

    def httpEntity: HttpEntity[String] = new HttpEntity(
      reqF.req.entity.map(_.cata[String](_.body, _.body.map(_.toChar).mkString, Function.const(""))).orNull,
      new LinkedMultiValueMap[String, String](reqF.req.headers.mapValues(List(_).asJava).asJava)
    )

    def requestEntity(httpMethod: HttpMethod): RequestEntity[String] =
      new RequestEntity[String](httpEntity.getBody, httpEntity.getHeaders, httpMethod, new URI(reqF.req.uri.show))

    (reqF match {
      case Get(_)     => requestEntity(HttpMethod.GET)
      case Delete(_)  => requestEntity(HttpMethod.DELETE)
      case Head(_)    => requestEntity(HttpMethod.HEAD)
      case Options(_) => requestEntity(HttpMethod.OPTIONS)
      case Post(_)    => requestEntity(HttpMethod.POST)
      case Put(_)     => requestEntity(HttpMethod.PUT)
      case Trace(_)   => requestEntity(HttpMethod.TRACE)
      case Patch(_)   => requestEntity(HttpMethod.PATCH)
    }).pure[F]
  }

  def execute[F[_]: Sync](rtRequest: RequestEntity[String])(implicit client: RestTemplate): F[ResponseEntity[String]] =
    Sync[F].delay { client.exchange(rtRequest, classOf[String]) }

  def mapResponse[F[_]: Applicative](response: ResponseEntity[String]): F[HttpResponse] = {

    def createEntity(response: ResponseEntity[String]): Entity = response.getHeaders.getContentType match {
      case MediaType.APPLICATION_OCTET_STREAM => Entity.ByteArrayEntity(response.getBody.getBytes)
      case _                                  => Entity.StringEntity(response.getBody)
    }

    HttpResponse(
      Status.Statuses(response.getStatusCodeValue),
      response.getHeaders.toSingleValueMap.asScala.toMap,
      createEntity(response)
    ).pure[F]
  }
} 
Example 35
Source File: AsyncHttpClientInterpreter.scala    From hammock   with MIT License 5 votes vote down vote up
package hammock
package asynchttpclient

import cats._
import cats.implicits._
import cats.data.Kleisli
import cats.effect._
import org.asynchttpclient._
import java.util.{concurrent => jc}
import scala.util._
import scala.collection.JavaConverters._

object AsyncHttpClientInterpreter {

  def apply[F[_]](implicit F: InterpTrans[F]): InterpTrans[F] = F

  implicit def instance[F[_]: Async](
      implicit client: AsyncHttpClient = new DefaultAsyncHttpClient()
  ): InterpTrans[F] = new InterpTrans[F] {
    override def trans: HttpF ~> F = transK andThen λ[Kleisli[F, AsyncHttpClient, *] ~> F](_.run(client))
  }

  def transK[F[_]: Async]: HttpF ~> Kleisli[F, AsyncHttpClient, *] = {

    def toF[A](future: jc.Future[A]): F[A] =
      Async[F].async(_(Try(future.get) match {
        case Failure(err) => Left(err)
        case Success(a)   => Right(a)
      }))

    λ[HttpF ~> Kleisli[F, AsyncHttpClient, *]] {
      case reqF @ (Get(_) | Options(_) | Delete(_) | Head(_) | Options(_) | Trace(_) | Post(_) | Put(_) | Patch(_)) =>
        Kleisli { implicit client =>
          for {
            req             <- mapRequest[F](reqF)
            ahcResponse     <- toF(req.execute())
            hammockResponse <- mapResponse[F](ahcResponse)
          } yield hammockResponse
        }
    }
  }

  def mapRequest[F[_]: Async](reqF: HttpF[HttpResponse])(implicit client: AsyncHttpClient): F[BoundRequestBuilder] = {

    def putHeaders(req: BoundRequestBuilder, headers: Map[String, String]): F[Unit] =
      Async[F].delay {
        req.setSingleHeaders(headers.map(kv => kv._1.asInstanceOf[CharSequence] -> kv._2).asJava)
      } *> ().pure[F]

    def getBuilder(reqF: HttpF[HttpResponse]): BoundRequestBuilder = reqF match {
      case Get(_)     => client.prepareGet(reqF.req.uri.show)
      case Delete(_)  => client.prepareDelete(reqF.req.uri.show)
      case Head(_)    => client.prepareHead(reqF.req.uri.show)
      case Options(_) => client.prepareOptions(reqF.req.uri.show)
      case Post(_)    => client.preparePost(reqF.req.uri.show)
      case Put(_)     => client.preparePut(reqF.req.uri.show)
      case Trace(_)   => client.prepareTrace(reqF.req.uri.show)
      case Patch(_)   => client.preparePatch(reqF.req.uri.show)
    }

    for {
      req <- getBuilder(reqF).pure[F]
      _   <- putHeaders(req, reqF.req.headers)
      _ = reqF.req.entity
        .foreach(_.cata(str => req.setBody(str.content), bytes => req.setBody(bytes.content), Function.const(())))
    } yield req
  }

  def mapResponse[F[_]: Applicative](ahcResponse: Response): F[HttpResponse] = {

    def createEntity(r: Response): Entity = r.getContentType match {
      case AsyncHttpClientContentType.`application/octet-stream` => Entity.ByteArrayEntity(r.getResponseBodyAsBytes)
      case _                                                     => Entity.StringEntity(r.getResponseBody)
    }

    HttpResponse(
      Status.Statuses(ahcResponse.getStatusCode),
      ahcResponse.getHeaders.names.asScala.map(name => (name, ahcResponse.getHeaders.get(name))).toMap,
      createEntity(ahcResponse)
    ).pure[F]
  }

} 
Example 36
Source File: algebras.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package freestyle.tagless

import cats.Id
import cats.data.Kleisli
import freestyle.tagless.logging.LoggingM

import scala.concurrent.Future

object algebras {

  @tagless(true)
  trait NonLogging {
    def x: FS[Int]
  }

  implicit def nonLoggingFutureHandler: NonLogging.Handler[Future] =
    new NonLogging.Handler[Future] {
      def x: Future[Int] = Future.successful(1)
    }

  type TestAlgebra[A] = Kleisli[Id, String, A]

  implicit def nonLoggingTestAlgebraHandler: NonLogging.Handler[TestAlgebra] =
    new NonLogging.Handler[TestAlgebra] {
      def x: TestAlgebra[Int] = Kleisli.pure(1)
    }

  @module
  trait App {
    val nonLogging: NonLogging
    val loggingM: LoggingM
  }
} 
Example 37
Source File: algebras.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package freestyle.free

import cats.Id
import cats.data.Kleisli
import freestyle.free.logging.LoggingM

import scala.concurrent.Future

object algebras {

  @free
  trait NonLogging {
    def x: FS[Int]
  }

  implicit def nonLoggingFutureHandler: NonLogging.Handler[Future] =
    new NonLogging.Handler[Future] {
      def x: Future[Int] = Future.successful(1)
    }

  type TestAlgebra[A] = Kleisli[Id, String, A]

  implicit def nonLoggingTestAlgebraHandler: NonLogging.Handler[TestAlgebra] =
    new NonLogging.Handler[TestAlgebra] {
      def x: TestAlgebra[Int] = Kleisli.pure(1)
    }

  @module
  trait App {
    val nonLogging: NonLogging
    val loggingM: LoggingM
  }

  val app = App[App.Op]
} 
Example 38
Source File: HmacAuthMiddleware.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.network.http.server.middleware

import java.time.{Duration, Instant}

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

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

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

object HmacAuthMiddleware {
  val defaultDuration: FiniteDuration = 5.minutes

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

  def apply[F[_]: Sync](key: MacSigningKey[HMACSHA256], duration: FiniteDuration = defaultDuration)(routes: HttpRoutes[F]): HttpRoutes[F] =
    Kleisli { req: Request[F] =>
      verifyFromHeader(req, key, duration) match {
        case Left(error) => OptionT.some[F](Response[F](Status.Forbidden).withEntity(error.message))
        case Right(_)    => routes(req)
      }
    }
} 
Example 39
Source File: HttpErrorHandler.scala    From codepropertygraph   with Apache License 2.0 5 votes vote down vote up
package io.shiftleft.cpgserver.route

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

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

object HttpErrorHandler {

  def apply(routes: HttpRoutes[IO])(handler: PartialFunction[Throwable, IO[Response[IO]]]): HttpRoutes[IO] = {
    Kleisli { req: Request[IO] =>
      OptionT {
        routes.run(req).value.handleErrorWith { e =>
          if (handler.isDefinedAt(e)) handler(e).map(Option(_))
          else IO.raiseError(e)
        }
      }
    }
  }
} 
Example 40
Source File: AckerConsumerDemo.scala    From fs2-rabbit   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.fs2rabbit.examples

import java.nio.charset.StandardCharsets.UTF_8

import cats.data.Kleisli
import cats.effect._
import cats.implicits._
import dev.profunktor.fs2rabbit.config.declaration.{DeclarationExchangeConfig, DeclarationQueueConfig}
import dev.profunktor.fs2rabbit.interpreter.RabbitClient
import dev.profunktor.fs2rabbit.json.Fs2JsonEncoder
import dev.profunktor.fs2rabbit.model.AckResult.Ack
import dev.profunktor.fs2rabbit.model.AmqpFieldValue.{LongVal, StringVal}
import dev.profunktor.fs2rabbit.model.ExchangeType.Topic
import dev.profunktor.fs2rabbit.model._
import fs2._

class AckerConsumerDemo[F[_]: Concurrent: Timer](fs2Rabbit: RabbitClient[F]) {
  private val queueName    = QueueName("testQ")
  private val exchangeName = ExchangeName("testEX")
  private val routingKey   = RoutingKey("testRK")

  implicit val stringMessageEncoder =
    Kleisli[F, AmqpMessage[String], AmqpMessage[Array[Byte]]](s => s.copy(payload = s.payload.getBytes(UTF_8)).pure[F])

  def logPipe: Pipe[F, AmqpEnvelope[String], AckResult] = _.evalMap { amqpMsg =>
    putStrLn(s"Consumed: $amqpMsg").as(Ack(amqpMsg.deliveryTag))
  }

  val publishingFlag: PublishingFlag = PublishingFlag(mandatory = true)

  // Run when there's no consumer for the routing key specified by the publisher and the flag mandatory is true
  val publishingListener: PublishReturn => F[Unit] = pr => putStrLn(s"Publish listener: $pr")

  private val mkChannel = fs2Rabbit.createConnection.flatMap(fs2Rabbit.createChannel)

  val program: F[Unit] = mkChannel.use { implicit channel =>
    for {
      _                 <- fs2Rabbit.declareQueue(DeclarationQueueConfig.default(queueName))
      _                 <- fs2Rabbit.declareExchange(DeclarationExchangeConfig.default(exchangeName, Topic))
      _                 <- fs2Rabbit.bindQueue(queueName, exchangeName, routingKey)
      (acker, consumer) <- fs2Rabbit.createAckerConsumer[String](queueName)
      publisher <- fs2Rabbit.createPublisherWithListener[AmqpMessage[String]](
                    exchangeName,
                    routingKey,
                    publishingFlag,
                    publishingListener
                  )
      _ <- new Flow[F, String](consumer, acker, logPipe, publisher).flow.compile.drain
    } yield ()
  }

}

class Flow[F[_]: Concurrent, A](
    consumer: Stream[F, AmqpEnvelope[A]],
    acker: AckResult => F[Unit],
    logger: Pipe[F, AmqpEnvelope[A], AckResult],
    publisher: AmqpMessage[String] => F[Unit]
) {

  import io.circe.generic.auto._

  case class Address(number: Int, streetName: String)
  case class Person(id: Long, name: String, address: Address)

  private val jsonEncoder = new Fs2JsonEncoder
  import jsonEncoder.jsonEncode

  val jsonPipe: Pipe[Pure, AmqpMessage[Person], AmqpMessage[String]] = _.map(jsonEncode[Person])

  val simpleMessage =
    AmqpMessage("Hey!", AmqpProperties(headers = Map("demoId" -> LongVal(123), "app" -> StringVal("fs2RabbitDemo"))))
  val classMessage = AmqpMessage(Person(1L, "Sherlock", Address(212, "Baker St")), AmqpProperties.empty)

  val flow: Stream[F, Unit] =
    Stream(
      Stream(simpleMessage).covary[F].evalMap(publisher),
      Stream(classMessage).covary[F].through(jsonPipe).evalMap(publisher),
      consumer.through(logger).evalMap(acker)
    ).parJoin(3)

} 
Example 41
Source File: AutoAckConsumerDemo.scala    From fs2-rabbit   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.fs2rabbit.examples

import java.nio.charset.StandardCharsets.UTF_8

import cats.data.Kleisli
import cats.effect._
import cats.implicits._
import dev.profunktor.fs2rabbit.config.declaration.DeclarationQueueConfig
import dev.profunktor.fs2rabbit.interpreter.RabbitClient
import dev.profunktor.fs2rabbit.json.Fs2JsonEncoder
import dev.profunktor.fs2rabbit.model.AckResult.Ack
import dev.profunktor.fs2rabbit.model.AmqpFieldValue.{LongVal, StringVal}
import dev.profunktor.fs2rabbit.model._
import fs2.{Pipe, Pure, Stream}
import io.circe.Encoder

class AutoAckConsumerDemo[F[_]: Concurrent](R: RabbitClient[F]) {
  private val queueName    = QueueName("testQ")
  private val exchangeName = ExchangeName("testEX")
  private val routingKey   = RoutingKey("testRK")
  implicit val stringMessageEncoder =
    Kleisli[F, AmqpMessage[String], AmqpMessage[Array[Byte]]](s => s.copy(payload = s.payload.getBytes(UTF_8)).pure[F])

  def logPipe: Pipe[F, AmqpEnvelope[String], AckResult] = _.evalMap { amqpMsg =>
    putStrLn(s"Consumed: $amqpMsg").as(Ack(amqpMsg.deliveryTag))
  }

  val program: F[Unit] = R.createConnectionChannel.use { implicit channel =>
    for {
      _         <- R.declareQueue(DeclarationQueueConfig.default(queueName))
      _         <- R.declareExchange(exchangeName, ExchangeType.Topic)
      _         <- R.bindQueue(queueName, exchangeName, routingKey)
      publisher <- R.createPublisher[AmqpMessage[String]](exchangeName, routingKey)
      consumer  <- R.createAutoAckConsumer[String](queueName)
      _         <- new AutoAckFlow[F, String](consumer, logPipe, publisher).flow.compile.drain
    } yield ()
  }

}

class AutoAckFlow[F[_]: Concurrent, A](
    consumer: Stream[F, AmqpEnvelope[A]],
    logger: Pipe[F, AmqpEnvelope[A], AckResult],
    publisher: AmqpMessage[String] => F[Unit]
) {

  import io.circe.generic.auto._

  case class Address(number: Int, streetName: String)
  case class Person(id: Long, name: String, address: Address)

  private def jsonEncoder = new Fs2JsonEncoder

  def encoderPipe[T: Encoder]: Pipe[F, AmqpMessage[T], AmqpMessage[String]] =
    _.map(jsonEncoder.jsonEncode[T])

  val jsonPipe: Pipe[Pure, AmqpMessage[Person], AmqpMessage[String]] = _.map(jsonEncoder.jsonEncode[Person])

  val simpleMessage =
    AmqpMessage("Hey!", AmqpProperties(headers = Map("demoId" -> LongVal(123), "app" -> StringVal("fs2RabbitDemo"))))
  val classMessage = AmqpMessage(Person(1L, "Sherlock", Address(212, "Baker St")), AmqpProperties.empty)

  val flow: Stream[F, Unit] =
    Stream(
      Stream(simpleMessage).covary[F].evalMap(publisher),
      Stream(classMessage).covary[F].through(encoderPipe[Person]).evalMap(publisher),
      consumer.through(logger).through(_.evalMap(putStrLn(_)))
    ).parJoin(3)

} 
Example 42
Source File: EnvelopeDecoder.scala    From fs2-rabbit   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.fs2rabbit.effects
import cats.{Applicative, ApplicativeError}
import cats.data.Kleisli
import dev.profunktor.fs2rabbit.model.{AmqpFieldValue, AmqpProperties, ExchangeName, RoutingKey}
import dev.profunktor.fs2rabbit.model.AmqpFieldValue._
import cats.implicits._

object EnvelopeDecoder {
  def apply[F[_], A](implicit e: EnvelopeDecoder[F, A]): EnvelopeDecoder[F, A] = e

  def properties[F[_]: Applicative]: EnvelopeDecoder[F, AmqpProperties] =
    Kleisli(e => e.properties.pure[F])

  def payload[F[_]: Applicative]: EnvelopeDecoder[F, Array[Byte]] =
    Kleisli(_.payload.pure[F])

  def routingKey[F[_]: Applicative]: EnvelopeDecoder[F, RoutingKey] =
    Kleisli(e => e.routingKey.pure[F])

  def exchangeName[F[_]: Applicative]: EnvelopeDecoder[F, ExchangeName] =
    Kleisli(e => e.exchangeName.pure[F])

  def redelivered[F[_]: Applicative]: EnvelopeDecoder[F, Boolean] =
    Kleisli(e => e.redelivered.pure[F])

  def header[F[_]](name: String)(implicit F: ApplicativeError[F, Throwable]): EnvelopeDecoder[F, AmqpFieldValue] =
    Kleisli(e => F.catchNonFatal(e.properties.headers(name)))

  def optHeader[F[_]: Applicative](name: String): EnvelopeDecoder[F, Option[AmqpFieldValue]] =
    Kleisli(_.properties.headers.get(name).pure[F])

  def stringHeader[F[_]: ApplicativeError[?[_], Throwable]](name: String): EnvelopeDecoder[F, String] =
    headerPF[F, String](name) { case StringVal(a) => a }

  def intHeader[F[_]: ApplicativeError[?[_], Throwable]](name: String): EnvelopeDecoder[F, Int] =
    headerPF[F, Int](name) { case IntVal(a) => a }

  def longHeader[F[_]: ApplicativeError[?[_], Throwable]](name: String): EnvelopeDecoder[F, Long] =
    headerPF[F, Long](name) { case LongVal(a) => a }

  def arrayHeader[F[_]: ApplicativeError[?[_], Throwable]](name: String): EnvelopeDecoder[F, collection.Seq[Any]] =
    headerPF[F, collection.Seq[Any]](name) { case ArrayVal(a) => a }

  def optStringHeader[F[_]: ApplicativeError[?[_], Throwable]](name: String): EnvelopeDecoder[F, Option[String]] =
    optHeaderPF[F, String](name) { case StringVal(a) => a }

  def optIntHeader[F[_]: ApplicativeError[?[_], Throwable]](name: String): EnvelopeDecoder[F, Option[Int]] =
    optHeaderPF[F, Int](name) { case IntVal(a) => a }

  def optLongHeader[F[_]: ApplicativeError[?[_], Throwable]](name: String): EnvelopeDecoder[F, Option[Long]] =
    optHeaderPF[F, Long](name) { case LongVal(a) => a }

  def optArrayHeader[F[_]: ApplicativeError[?[_], Throwable]](
      name: String): EnvelopeDecoder[F, Option[collection.Seq[Any]]] =
    optHeaderPF[F, collection.Seq[Any]](name) { case ArrayVal(a) => a }

  private def headerPF[F[_], A](name: String)(pf: PartialFunction[AmqpFieldValue, A])(
      implicit F: ApplicativeError[F, Throwable]): EnvelopeDecoder[F, A] =
    Kleisli { env =>
      F.catchNonFatal(pf(env.properties.headers(name)))
    }

  private def optHeaderPF[F[_], A](name: String)(pf: PartialFunction[AmqpFieldValue, A])(
      implicit F: ApplicativeError[F, Throwable]): EnvelopeDecoder[F, Option[A]] =
    Kleisli(_.properties.headers.get(name).traverse(h => F.catchNonFatal(pf(h))))
} 
Example 43
Source File: ConversionResult.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.convert

import cats.data.Kleisli
import fs2.Stream

import docspell.common.MimeType

sealed trait ConversionResult[F[_]] {

  def pdfData: Stream[F, Byte]

}

object ConversionResult {

  
  type Handler[F[_], A] = Kleisli[F, ConversionResult[F], A]

  def unsupportedFormat[F[_]](mime: MimeType): ConversionResult[F] =
    UnsupportedFormat[F](mime)

  def failure[F[_]](ex: Throwable): ConversionResult[F] =
    Failure[F](ex)

  def successPdf[F[_]](pdf: Stream[F, Byte]): ConversionResult[F] =
    SuccessPdf[F](pdf)

  def successPdfTxt[F[_]](pdf: Stream[F, Byte], txt: F[String]): ConversionResult[F] =
    SuccessPdfTxt[F](pdf, txt)

  def inputMalformed[F[_]](mimeType: MimeType, reason: String): ConversionResult[F] =
    InputMalformed(mimeType, reason)

  case class UnsupportedFormat[F[_]](mime: MimeType) extends ConversionResult[F] {
    val pdfData = Stream.empty
  }
  case class Failure[F[_]](ex: Throwable) extends ConversionResult[F] {
    val pdfData = Stream.empty
  }
  case class SuccessPdf[F[_]](pdf: Stream[F, Byte]) extends ConversionResult[F] {
    val pdfData = pdf
  }
  case class SuccessPdfTxt[F[_]](pdf: Stream[F, Byte], txt: F[String])
      extends ConversionResult[F] {
    val pdfData = pdf
  }

  case class InputMalformed[F[_]](mimeType: MimeType, reason: String)
      extends ConversionResult[F] {
    val pdfData = Stream.empty
  }
} 
Example 44
Source File: FileChecks.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.convert

import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Path}

import cats.data.Kleisli
import cats.effect.IO
import fs2.{Pipe, Stream}
import docspell.common.MimeType
import docspell.convert.ConversionResult.Handler
import docspell.files.TikaMimetype

trait FileChecks {

  implicit class FileCheckOps(p: Path) {

    def isNonEmpty: Boolean =
      Files.exists(p) && Files.size(p) > 0

    def isType(mime: MimeType): Boolean =
      TikaMimetype.detect[IO](p).map(_ == mime).unsafeRunSync

    def isPDF: Boolean =
      isType(MimeType.pdf)

    def isPlainText: Boolean =
      isType(MimeType.text("plain"))
  }

  def storeFile(file: Path): Pipe[IO, Byte, Path] =
    in => Stream.eval(in.compile.to(Array).flatMap(bytes => IO(Files.write(file, bytes))))

  def storePdfHandler(file: Path): Handler[IO, Path] =
    storePdfTxtHandler(file, file.resolveSibling("unexpected.txt")).map(_._1)

  def storePdfTxtHandler(filePdf: Path, fileTxt: Path): Handler[IO, (Path, Path)] =
    Kleisli({
      case ConversionResult.SuccessPdfTxt(pdf, txt) =>
        for {
          pout <- pdf.through(storeFile(filePdf)).compile.lastOrError
          str  <- txt
          tout <- IO(Files.write(fileTxt, str.getBytes(StandardCharsets.UTF_8)))
        } yield (pout, tout)

      case ConversionResult.SuccessPdf(pdf) =>
        pdf.through(storeFile(filePdf)).compile.lastOrError.map(p => (p, fileTxt))

      case ConversionResult.Failure(ex) =>
        throw new Exception(s"Unexpected result (failure: ${ex.getMessage})", ex)

      case cr =>
        throw new Exception(s"Unexpected result: $cr")
    })

  def commandExists(cmd: String): Boolean =
    Runtime.getRuntime.exec(Array("which", cmd)).waitFor() == 0

} 
Example 45
Source File: Migration.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.joex.fts

import cats.Traverse
import cats.data.{Kleisli, OptionT}
import cats.effect._
import cats.implicits._

import docspell.common._
import docspell.ftsclient._
import docspell.joex.Config
import docspell.store.records.RFtsMigration
import docspell.store.{AddResult, Store}

case class Migration[F[_]](
    version: Int,
    engine: Ident,
    description: String,
    task: FtsWork[F]
)

object Migration {

  def apply[F[_]: Effect](
      cfg: Config.FullTextSearch,
      fts: FtsClient[F],
      store: Store[F],
      logger: Logger[F]
  ): Kleisli[F, List[Migration[F]], Unit] = {
    val ctx = FtsContext(cfg, store, fts, logger)
    Kleisli(migs => Traverse[List].sequence(migs.map(applySingle[F](ctx))).map(_ => ()))
  }

  def applySingle[F[_]: Effect](ctx: FtsContext[F])(m: Migration[F]): F[Unit] = {
    val insertRecord: F[Option[RFtsMigration]] =
      for {
        rec <- RFtsMigration.create(m.version, m.engine, m.description)
        res <- ctx.store.add(
          RFtsMigration.insert(rec),
          RFtsMigration.exists(m.version, m.engine)
        )
        ret <- res match {
          case AddResult.Success         => rec.some.pure[F]
          case AddResult.EntityExists(_) => None.pure[F]
          case AddResult.Failure(ex)     => Effect[F].raiseError(ex)
        }
      } yield ret

    (for {
      _   <- OptionT.liftF(ctx.logger.info(s"Apply ${m.version}/${m.description}"))
      rec <- OptionT(insertRecord)
      res <- OptionT.liftF(m.task.run(ctx).attempt)
      ret <- OptionT.liftF(res match {
        case Right(()) => ().pure[F]
        case Left(ex) =>
          ctx.logger.error(ex)(
            s"Applying index migration ${m.version}/${m.description} failed"
          ) *>
            ctx.store.transact(RFtsMigration.deleteById(rec.id)) *> Effect[F]
            .raiseError[Unit](
              ex
            )
      })
    } yield ret).getOrElseF(
      ctx.logger.info(s"Migration ${m.version}/${m.description} already applied.")
    )
  }
} 
Example 46
Source File: FtsWork.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.joex.fts

import cats.data.{Kleisli, NonEmptyList}
import cats.effect._
import cats.implicits._
import cats.{ApplicativeError, FlatMap, Semigroup}

import docspell.common._
import docspell.ftsclient._
import docspell.joex.Config
import docspell.joex.scheduler.Context
import docspell.store.queries.{QAttachment, QItem}

object FtsWork {
  def apply[F[_]](f: FtsContext[F] => F[Unit]): FtsWork[F] =
    Kleisli(f)

  def all[F[_]: FlatMap](
      m0: FtsWork[F],
      mn: FtsWork[F]*
  ): FtsWork[F] =
    NonEmptyList.of(m0, mn: _*).reduce(semigroup[F])

  implicit def semigroup[F[_]: FlatMap]: Semigroup[FtsWork[F]] =
    Semigroup.instance((mt1, mt2) => mt1.flatMap(_ => mt2))

  // some tasks

  def log[F[_]](f: Logger[F] => F[Unit]): FtsWork[F] =
    FtsWork(ctx => f(ctx.logger))

  def initialize[F[_]]: FtsWork[F] =
    FtsWork(_.fts.initialize)

  def clearIndex[F[_]](coll: Option[Ident]): FtsWork[F] =
    coll match {
      case Some(cid) =>
        FtsWork(ctx => ctx.fts.clear(ctx.logger, cid))
      case None =>
        FtsWork(ctx => ctx.fts.clearAll(ctx.logger))
    }

  def insertAll[F[_]: Effect](coll: Option[Ident]): FtsWork[F] =
    FtsWork
      .all(
        FtsWork(ctx =>
          ctx.fts.indexData(
            ctx.logger,
            ctx.store
              .transact(
                QAttachment
                  .allAttachmentMetaAndName(coll, ctx.cfg.migration.indexAllChunk)
              )
              .map(caa =>
                TextData
                  .attachment(
                    caa.item,
                    caa.id,
                    caa.collective,
                    caa.lang,
                    caa.name,
                    caa.content
                  )
              )
          )
        ),
        FtsWork(ctx =>
          ctx.fts.indexData(
            ctx.logger,
            ctx.store
              .transact(QItem.allNameAndNotes(coll, ctx.cfg.migration.indexAllChunk * 5))
              .map(nn => TextData.item(nn.id, nn.collective, Option(nn.name), nn.notes))
          )
        )
      )

  object syntax {
    implicit final class FtsWorkOps[F[_]](mt: FtsWork[F]) {
      def ++(mn: FtsWork[F])(implicit ev: FlatMap[F]): FtsWork[F] =
        all(mt, mn)

      def recoverWith(
          other: FtsWork[F]
      )(implicit ev: ApplicativeError[F, Throwable]): FtsWork[F] =
        Kleisli(ctx => mt.run(ctx).onError({ case _ => other.run(ctx) }))

      def forContext(
          cfg: Config.FullTextSearch,
          fts: FtsClient[F]
      ): Kleisli[F, Context[F, _], Unit] =
        mt.local(ctx => FtsContext(cfg, fts, ctx))
    }
  }
} 
Example 47
Source File: Tracer.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
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 48
Source File: AuthTracedHttpRoute.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer.auth

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

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

  def apply[T, F[_]: Monad: Tracer](
      pf: PartialFunction[AuthTracedRequest[F, T], F[Response[F]]]
  ): AuthedRoutes[T, F] =
    Kleisli[OptionT[F, ?], AuthedRequest[F, T], Response[F]] { req =>
      OptionT {
        Tracer[F]
          .getTraceId(req.req)
          .map(x => AuthTracedRequest[F, T](x.getOrElse(TraceId("-")), req))
          .flatMap { tr =>
            val rs: OptionT[F, Response[F]] = pf.andThen(OptionT.liftF(_)).applyOrElse(tr, Function.const(OptionT.none))
            rs.value
          }
      }
    }
} 
Example 49
Source File: TracedHttpRoute.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer

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

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

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

    }

}