cats.effect.Effect Scala Examples

The following examples show how to use cats.effect.Effect. 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: MetricsMiddleware.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.network.http.server.middleware

import cats.effect.{Clock, Effect, Sync}
import cats.implicits._
import jbok.common.metrics.PrometheusMetrics
import org.http4s.HttpRoutes
import org.http4s.metrics.prometheus.{Prometheus, PrometheusExportService}
import org.http4s.server.middleware

object MetricsMiddleware {
  def exportService[F[_]](implicit F: Sync[F]): F[HttpRoutes[F]] =
    for {
      _ <- PrometheusExportService.addDefaults[F](PrometheusMetrics.registry)
    } yield PrometheusExportService.service[F](PrometheusMetrics.registry)

  def apply[F[_]](routes: HttpRoutes[F], enableMetrics: Boolean)(implicit F: Effect[F], clock: Clock[F]): F[HttpRoutes[F]] =
    if (enableMetrics) {
      Prometheus[F](PrometheusMetrics.registry, "iotchain_http_server").map { metricsOps =>
        middleware.Metrics[F](metricsOps)(routes)
      }
    } else {
      F.pure(routes)
    }
} 
Example 2
Source File: TracingUtils.scala    From opencensus-scala   with Apache License 2.0 5 votes vote down vote up
package io.opencensus.scala.http4s

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

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

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

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

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

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

    Client(tracedOpen)
  }

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

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

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

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

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

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

import akka.http.scaladsl.marshalling.ToEntityMarshaller
import akka.http.scaladsl.model.StatusCode
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives.{path => akkaPath, _}
import akka.http.scaladsl.server.Route
import cats.effect.Effect
import cats.syntax.functor._
import cats.syntax.reducible._
import cats.~>
import cats.Functor
import cats.Reducible
import sup.HealthCheck
import sup.HealthResult

import scala.concurrent.Future
import scala.util.Failure
import scala.util.Success
import akka.http.scaladsl.model.HttpRequest

object akkahttp {

  
  def healthCheckRoutes[F[_]: Effect, H[_]: Reducible](
    healthCheck: HealthCheck[F, H],
    path: String = "health-check"
  )(
    implicit marshaller: ToEntityMarshaller[HealthResult[H]]
  ): Route =
    akkaPath(path) {
      get {
        onComplete(Effect[F].toIO(healthCheckResponse(healthCheck)).unsafeToFuture()) {
          case Success(response) => complete(response)
          case Failure(error)    => failWith(error)
        }
      }
    }

  def healthCheckResponse[F[_]: Functor, H[_]: Reducible](
    healthCheck: HealthCheck[F, H]
  ): F[(StatusCode, HealthResult[H])] =
    healthCheck.check.map { check =>
      if (check.value.reduce.isHealthy) StatusCodes.OK -> check
      else StatusCodes.ServiceUnavailable -> check
    }

  def healthCheckRoutesWithContext[F[_]: Functor, H[_]: Reducible, R](
    healthCheck: HealthCheck[F, H],
    path: String = "health-check"
  )(
    run: HttpRequest => F ~> Future
  )(
    implicit marshaller: ToEntityMarshaller[HealthResult[H]]
  ): Route =
    akkaPath(path) {
      get {
        extractRequest { request =>
          onComplete(run(request)(healthCheckResponse(healthCheck))) {
            case Success(response) => complete(response)
            case Failure(error)    => failWith(error)
          }
        }
      }
    }
} 
Example 5
Source File: KamonSupport.scala    From kamon-http4s   with Apache License 2.0 5 votes vote down vote up
package kamon.http4s
package middleware.client

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

object KamonSupport {

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

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

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


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

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


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

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

} 
Example 6
Source File: TaskInstances.scala    From shims   with Apache License 2.0 5 votes vote down vote up
package shims.effect.instances

import cats.{Applicative, Monad, Parallel, StackSafeMonad, ~>}
import cats.effect.{Effect, ExitCase, IO, SyncIO}

import scalaz.{Tag, -\/, \/, \/-}
import scalaz.concurrent.Task.ParallelTask
import scalaz.concurrent.{Future, Task}

import shims.conversions.MonadErrorConversions

import java.util.concurrent.atomic.AtomicBoolean

import scala.util.control.NonFatal

trait TaskInstances extends MonadErrorConversions {

  // cribbed from quasar, where it was mostly cribbed from scalaz-task-effect
  implicit object taskEffect extends Effect[Task] with StackSafeMonad[Task] {

    def pure[A](x: A): Task[A] = Task.now(x)

    def handleErrorWith[A](fa: Task[A])(f: Throwable => Task[A]): Task[A] =
      fa.handleWith(functionToPartial(f))

    def raiseError[A](e: Throwable): Task[A] = Task.fail(e)

    // In order to comply with `repeatedCallbackIgnored` law
    // on async, a custom AtomicBoolean is required to ignore
    // second callbacks.
    def async[A](k: (Either[Throwable, A] => Unit) => Unit): Task[A] = Task.async { registered =>
      val a = new AtomicBoolean(true)
      try k(e => if (a.getAndSet(false)) registered(\/.fromEither(e)) else ())
      catch { case NonFatal(t) => registered(-\/(t)) }
    }

    def asyncF[A](k: (Either[Throwable, A] => Unit) => Task[Unit]): Task[A] =
      async(k.andThen(_.unsafePerformAsync(forget)))

    // emulates using attempt
    def bracketCase[A, B](acquire: Task[A])(use: A => Task[B])(release: (A, ExitCase[Throwable]) => Task[Unit]): Task[B] = {
      for {
        a <- acquire
        bOr <- use(a).attempt
        ec = bOr.fold(ExitCase.Error(_), _ => ExitCase.Completed)
        _ <- release(a, ec)
        b <- bOr.fold(Task.fail, Task.now)
      } yield b
    }

    
    def runAsync[A](fa: Task[A])(cb: Either[Throwable, A] => IO[Unit]): SyncIO[Unit] =
      SyncIO {
        fa unsafePerformAsync { disjunction =>
          cb(disjunction.toEither).unsafeRunAsync(forget)
        }
      }

    def runSyncStep[A](fa: Task[A]): IO[Either[Task[A], A]] =
      IO {
        fa.get match {
          case Future.Now(-\/(_)) => Left(fa)

          case other => other.step match {
            case Future.Now(\/-(a)) => Right(a)
            case other => Left(new Task(other))
          }
        }
      }

    override def map[A, B](fa: Task[A])(f: A => B): Task[B] =
      fa.map(f)

    def flatMap[A, B](fa: Task[A])(f: A => Task[B]): Task[B] = fa.flatMap(f)

    override def delay[A](thunk: => A): Task[A] = Task.delay(thunk)

    def suspend[A](thunk: => Task[A]): Task[A] = Task.suspend(thunk)
  }

  implicit val taskParallel: Parallel.Aux[Task, ParallelTask] = new Parallel[Task] {
    import Task.taskParallelApplicativeInstance

    type F[A] = ParallelTask[A]

    val monad: Monad[Task] = taskEffect
    val applicative: Applicative[ParallelTask] = Applicative[ParallelTask]
    val sequential: ParallelTask ~> Task = λ[ParallelTask ~> Task](Tag.unwrap(_))
    val parallel: Task ~> ParallelTask = λ[Task ~> ParallelTask](Tag(_))
  }

  private def functionToPartial[A, B](f: A => B): PartialFunction[A, B] = {
    case a => f(a)
  }

  private def forget[A](x: A): Unit = ()
} 
Example 7
Source File: package.scala    From finch-oauth2   with Apache License 2.0 5 votes vote down vote up
package io.finch

import cats.effect.Effect
import cats.implicits._
import com.twitter.finagle.OAuth2
import com.twitter.finagle.http.Status
import com.twitter.finagle.oauth2.{AuthInfo, DataHandler, GrantResult, OAuthError}
import com.twitter.util.{Future => TwitterFuture, Return, Throw}

package object oauth2 {

  private def twitterFutureToEffect[F[_], A](f: => TwitterFuture[A])(implicit F: Effect[F]): F[A] =
    F.async { cb =>
      f.respond {
        case Return(r) => cb(Right(r))
        case Throw(t) => cb(Left(t))
      }
    }

  private val handleOAuthError: PartialFunction[Throwable, Output[Nothing]] = {
    case e: OAuthError =>
      val bearer = Seq("error=\"" + e.errorType + "\"") ++
        (if (!e.description.isEmpty) Seq("error_description=\"" + e.description + "\"") else Nil)

      Output.failure(e, Status(e.statusCode))
        .withHeader("WWW-Authenticate" -> s"Bearer ${bearer.mkString(", ")}")
  }

  
  def issueAccessToken[F[_], U](dataHandler: DataHandler[U])(
    implicit F: Effect[F]
  ): Endpoint[F, GrantResult] =
    new Endpoint[F, GrantResult] {
      final def apply(input: Input): Endpoint.Result[F, GrantResult] = {
        val out = twitterFutureToEffect(OAuth2.issueAccessToken(input.request, dataHandler))
          .map(ai => Output.payload(ai))
          .recover(handleOAuthError)

        EndpointResult.Matched(input, Trace.empty, out)
      }
    }
} 
Example 8
Source File: RerunnableInstances.scala    From catbird   with Apache License 2.0 5 votes vote down vote up
package io.catbird.util.effect

import cats.effect.{ Effect, ExitCase, IO, SyncIO }
import com.twitter.util.{ Future, Monitor, Promise, Return, Throw }
import io.catbird.util.{ Rerunnable, RerunnableMonadError }
import java.lang.Throwable
import scala.Unit
import scala.util.{ Either, Left, Right }

trait RerunnableInstances {
  implicit final val rerunnableEffectInstance: Effect[Rerunnable] =
    new RerunnableMonadError with Effect[Rerunnable] {
      final def suspend[A](thunk: => Rerunnable[A]): Rerunnable[A] = Rerunnable.suspend[A](thunk)

      override final def delay[A](thunk: => A): Rerunnable[A] = Rerunnable[A](thunk)

      final def async[A](k: (Either[Throwable, A] => Unit) => Unit): Rerunnable[A] =
        new Rerunnable[A] {
          final def run: Future[A] = {
            val promise = new Promise[A]

            k { e =>
              if (promise.isDefined) ()
              else
                e match {
                  case Right(a)  => promise.setValue(a)
                  case Left(err) => promise.setException(err)
                }
            }

            promise
          }
        }

      final def asyncF[A](k: (Either[Throwable, A] => Unit) => Rerunnable[Unit]): Rerunnable[A] =
        new Rerunnable[A] {
          final def run: Future[A] = {
            val promise = new Promise[A]

            val rerunnable = k { e =>
              if (promise.isDefined) ()
              else
                e match {
                  case Right(a)  => promise.setValue(a)
                  case Left(err) => promise.setException(err)
                }
            }

            rerunnable.run.flatMap(_ => promise)
          }
        }

      final def runAsync[A](fa: Rerunnable[A])(cb: Either[Throwable, A] => IO[Unit]): SyncIO[Unit] =
        rerunnableToIO[A](fa).runAsync(cb)

      final def bracketCase[A, B](acquire: Rerunnable[A])(use: A => Rerunnable[B])(
        release: (A, ExitCase[Throwable]) => Rerunnable[Unit]
      ): Rerunnable[B] = new Rerunnable[B] {
        final def run: Future[B] =
          acquire.run.flatMap { a =>
            val future = use(a).run
            future.transform {
              case Return(b)  => release(a, ExitCase.complete).run.handle(Monitor.catcher).flatMap(_ => future)
              case Throw(err) => release(a, ExitCase.error(err)).run.handle(Monitor.catcher).flatMap(_ => future)
            }
          }
      }
    }
} 
Example 9
Source File: TodoItemApi.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package examples.todolist
package http

import cats.effect.Effect
import cats.implicits._
import examples.todolist.service.TodoItemService
import io.circe.generic.auto._
import io.circe.syntax._
import org.http4s._
import org.http4s.circe._
import org.http4s.dsl.Http4sDsl

class TodoItemApi[F[_]: Effect](implicit service: TodoItemService[F]) extends Http4sDsl[F] {

  import codecs._

  private val prefix = "items"

  val endpoints = HttpService[F] {
    case POST -> Root / prefix =>
      service.reset *> Ok()

    case GET -> Root / prefix / IntVar(id) =>
      service.retrieve(id) flatMap { item =>
        item.fold(NotFound(s"Could not find ${service.model} with $id"))(todoItem =>
          Ok(todoItem.asJson))
      }

    case GET -> Root / prefix =>
      service.list.flatMap(l => Ok(l.asJson))

    case req @ POST -> Root / prefix =>
      for {
        todoItem     <- req.as[TodoItem]
        insertedItem <- service.insert(todoItem)
        response     <- Ok(insertedItem.asJson)
      } yield response

    case req @ PUT -> Root / prefix / IntVar(id) =>
      for {
        todoItem    <- req.as[TodoItem]
        updatedItem <- service.update(todoItem.copy(id = Some(id)))
        reponse     <- Ok(updatedItem.asJson)
      } yield reponse

    case DELETE -> Root / prefix / IntVar(id) =>
      service.destroy(id) *> Ok()
  }
}

object TodoItemApi {
  implicit def instance[F[_]: Effect](implicit service: TodoItemService[F]): TodoItemApi[F] =
    new TodoItemApi[F]
} 
Example 10
Source File: AppApi.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package examples.todolist
package http

import cats.effect.Effect
import cats.implicits._
import examples.todolist.service.AppService
import io.circe.generic.auto._
import io.circe.syntax._
import org.http4s._
import org.http4s.circe._
import org.http4s.dsl.Http4sDsl

class AppApi[F[_]: Effect](implicit service: AppService[F]) extends Http4sDsl[F] {

  import codecs._

  val endpoints = HttpService[F] {
    case POST -> Root / "reset" =>
      service.reset.flatMap(e => Ok(e.asJson))

    case GET -> Root / "list" =>
      service.list.flatMap(l => Ok(l.asJson))

    case req @ POST -> Root / "insert" =>
      for {
        form         <- req.as[TodoForm]
        insertedForm <- service.insert(form)
        response     <- Ok(insertedForm.asJson)
      } yield response

    case req @ PUT -> Root / "update" =>
      for {
        form        <- req.as[TodoForm]
        updatedForm <- service.update(form)
        response    <- Ok(updatedForm.asJson)
      } yield response

    case req @ DELETE -> Root / "delete" =>
      for {
        form     <- req.as[TodoForm]
        deleted  <- service.destroy(form)
        response <- Ok(deleted.asJson)
      } yield response
  }
}

object AppApi {
  implicit def instance[F[_]: Effect](implicit service: AppService[F]): AppApi[F] = new AppApi[F]
} 
Example 11
Source File: Api.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package examples.todolist
package http

import cats.effect.Effect
import cats.implicits._
import examples.todolist.http._
import org.http4s.implicits._

class Api[F[_]: Effect](
    implicit appApi: AppApi[F],
    genericApi: GenericApi[F],
    todoItemApi: TodoItemApi[F],
    todoListApi: TodoListApi[F],
    tagApi: TagApi[F]) {

  val endpoints =
    appApi.endpoints <+>
      genericApi.endpoints <+>
      todoItemApi.endpoints <+>
      todoListApi.endpoints <+>
      tagApi.endpoints
}

object Api {
  implicit def instance[F[_]: Effect](
      implicit appApi: AppApi[F],
      genericApi: GenericApi[F],
      todoItemApi: TodoItemApi[F],
      todoListApi: TodoListApi[F],
      tagApi: TagApi[F]): Api[F] =
    new Api[F]
} 
Example 12
Source File: TagApi.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package examples.todolist
package http

import cats.effect.Effect
import cats.implicits._
import examples.todolist.service.TagService
import examples.todolist.Tag
import io.circe.generic.auto._
import io.circe.syntax._
import org.http4s._
import org.http4s.circe._
import org.http4s.dsl.Http4sDsl

class TagApi[F[_]: Effect](implicit service: TagService[F]) extends Http4sDsl[F] {

  import codecs._

  private val prefix = "tags"

  val endpoints = HttpService[F] {
    case POST -> Root / prefix / "reset" =>
      service.reset.flatMap(e => Ok(e.asJson))

    case GET -> Root / prefix / IntVar(id) =>
      service.retrieve(id) flatMap { item =>
        item.fold(NotFound(s"Could not find ${service.model} with $id"))(tag => Ok(tag.asJson))
      }

    case GET -> Root / prefix =>
      service.list.flatMap(l => Ok(l.asJson))

    case req @ POST -> Root / prefix =>
      for {
        tag         <- req.as[Tag]
        insertedTag <- service.insert(tag)
        response    <- Ok(insertedTag.asJson)
      } yield response

    case req @ PUT -> Root / prefix / IntVar(id) =>
      for {
        tag        <- req.as[Tag]
        updatedTag <- service.update(tag.copy(id = Some(id)))
        reponse    <- Ok(updatedTag.asJson)
      } yield reponse

    case DELETE -> Root / prefix / IntVar(id) =>
      service.destroy(id) *> Ok()
  }
}

object TagApi {

  implicit def instance[F[_]: Effect](implicit service: TagService[F]): TagApi[F] = new TagApi[F]
} 
Example 13
Source File: TodoListApi.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package examples.todolist
package http

import cats.effect.Effect
import cats.implicits._
import examples.todolist.service.TodoListService
import io.circe.generic.auto._
import io.circe.syntax._
import org.http4s._
import org.http4s.circe._
import org.http4s.dsl.Http4sDsl

class TodoListApi[F[_]: Effect](implicit service: TodoListService[F]) extends Http4sDsl[F] {

  import codecs._

  private val prefix = "lists"

  val endpoints = HttpService[F] {
    case POST -> Root / prefix =>
      service.reset.flatMap(e => Ok(e.asJson))

    case GET -> Root / prefix / IntVar(id) =>
      service.retrieve(id) flatMap { item =>
        item.fold(NotFound(s"Could not find ${service.model} with $id"))(todoList =>
          Ok(todoList.asJson))
      }

    case GET -> Root / prefix =>
      service.list.flatMap(l => Ok(l.asJson))

    case req @ POST -> Root / prefix =>
      for {
        todoList         <- req.as[TodoList]
        insertedTodoList <- service.insert(todoList)
        response         <- Ok(insertedTodoList.asJson)
      } yield response

    case req @ PUT -> Root / prefix / IntVar(id) =>
      for {
        todoList        <- req.as[TodoList]
        updatedTodoList <- service.update(todoList.copy(id = Some(id)))
        reponse         <- Ok(updatedTodoList.asJson)
      } yield reponse

    case DELETE -> Root / prefix / IntVar(id) =>
      service.destroy(id) *> Ok()
  }
}

object TodoListApi {
  implicit def instance[F[_]: Effect](implicit service: TodoListService[F]): TodoListApi[F] =
    new TodoListApi[F]
} 
Example 14
Source File: GenericApi.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package examples.todolist
package http

import cats.effect.Effect
import cats.implicits._
import examples.todolist.model.Pong
import freestyle.tagless.logging.LoggingM
import io.circe.Json
import org.http4s.circe._
import org.http4s.dsl.Http4sDsl
import org.http4s.HttpService

class GenericApi[F[_]: Effect](implicit log: LoggingM[F]) extends Http4sDsl[F] {
  val endpoints =
    HttpService[F] {
      case GET -> Root / "ping" =>
        for {
          _        <- log.error("Not really an error")
          _        <- log.warn("Not really a warn")
          _        <- log.debug("GET /ping")
          response <- Ok(Json.fromLong(Pong.current.time))
        } yield response

      case GET -> Root / "hello" =>
        for {
          _        <- log.error("Not really an error")
          _        <- log.warn("Not really a warn")
          _        <- log.debug("GET /Hello")
          response <- Ok("Hello World")
        } yield response
    }
}

object GenericApi {
  implicit def instance[F[_]: Effect](implicit log: LoggingM[F]): GenericApi[F] =
    new GenericApi[F]
} 
Example 15
Source File: TodoListApp.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package todo

import cats.effect.{Effect, IO}
import cats.syntax.either._
import cats.syntax.flatMap._
import cats.syntax.functor._
import doobie.util.transactor.Transactor
import examples.todolist.http.Api
import examples.todolist.services.Services
import exapmles.todolist.peristence.Persistence
import freestyle.tagless.config.ConfigM
import freestyle.tagless.config.implicits._
import freestyle.tagless.effects.error.ErrorM
import freestyle.tagless.effects.error.implicits._
import freestyle.tagless.logging.LoggingM
import freestyle.tagless.loggingJVM.log4s.implicits._
import freestyle.tagless.module
import fs2.StreamApp
import org.http4s.HttpService
import org.http4s.implicits._
import org.http4s.server.blaze.BlazeBuilder

@module
trait App[F[_]] {
  val persistence: Persistence[F]
  val services: Services[F]
}

object TodoListApp extends StreamApp[IO] {

  import examples.todolist.runtime.implicits._

  override def stream(
      args: List[String],
      requestShutdown: IO[Unit]): fs2.Stream[IO, StreamApp.ExitCode] =
    bootstrap[IO].unsafeRunSync()

  def bootstrap[F[_]: Effect](
      implicit app: App[F],
      T: Transactor[F],
      api: Api[F]): F[fs2.Stream[F, StreamApp.ExitCode]] = {

    val services: HttpService[F] = api.endpoints
    val log: LoggingM[F]         = app.services.log
    val config: ConfigM[F]       = app.services.config

    for {
      _   <- log.info("Trying to load application.conf")
      cfg <- config.load
      host: String = cfg.string("http.host").getOrElse("localhost")
      port: Int    = cfg.int("http.port").getOrElse(8080)
      _ <- log.debug(s"Host: $host")
      _ <- log.debug(s"Port: $port")
    } yield
      BlazeBuilder[F]
        .bindHttp(port, host)
        .mountService(services)
        .serve
  }
} 
Example 16
Source File: Fs2StreamClientCallListener.scala    From fs2-grpc   with MIT License 5 votes vote down vote up
package org.lyranthe.fs2_grpc
package java_runtime
package client

import cats.effect.{Effect, ConcurrentEffect}
import cats.implicits._
import fs2.{Pull, Stream}
import fs2.concurrent.Queue
import io.grpc.{ClientCall, Metadata, Status}

class Fs2StreamClientCallListener[F[_]: Effect, Response](
    request: Int => Unit,
    queue: Queue[F, Either[GrpcStatus, Response]]
) extends ClientCall.Listener[Response] {

  override def onMessage(message: Response): Unit = {
    request(1)
    queue.enqueue1(message.asRight).unsafeRun()
  }

  override def onClose(status: Status, trailers: Metadata): Unit =
    queue.enqueue1(GrpcStatus(status, trailers).asLeft).unsafeRun()

  def stream: Stream[F, Response] = {

    def go(q: Stream[F, Either[GrpcStatus, Response]]): Pull[F, Response, Unit] = {
      q.pull.uncons1.flatMap {
        case Some((Right(v), tl)) => Pull.output1(v) >> go(tl)
        case Some((Left(GrpcStatus(status, trailers)), _)) =>
          if (!status.isOk)
            Pull.raiseError[F](status.asRuntimeException(trailers))
          else
            Pull.done
        case None => Pull.done
      }
    }

    go(queue.dequeue).stream
  }
}

object Fs2StreamClientCallListener {

  def apply[F[_]: ConcurrentEffect, Response](request: Int => Unit): F[Fs2StreamClientCallListener[F, Response]] =
    Queue.unbounded[F, Either[GrpcStatus, Response]].map(new Fs2StreamClientCallListener[F, Response](request, _))

} 
Example 17
Source File: Fs2StreamServerCallListener.scala    From fs2-grpc   with MIT License 5 votes vote down vote up
package org.lyranthe.fs2_grpc
package java_runtime
package server

import cats.effect.concurrent.Deferred
import cats.effect.{ConcurrentEffect, Effect}
import cats.implicits._
import io.grpc.ServerCall
import fs2.concurrent.Queue
import fs2._

class Fs2StreamServerCallListener[F[_], Request, Response] private (
    requestQ: Queue[F, Option[Request]],
    val isCancelled: Deferred[F, Unit],
    val call: Fs2ServerCall[F, Request, Response]
)(implicit F: Effect[F])
    extends ServerCall.Listener[Request]
    with Fs2ServerCallListener[F, Stream[F, ?], Request, Response] {

  override def onCancel(): Unit = {
    isCancelled.complete(()).unsafeRun()
  }

  override def onMessage(message: Request): Unit = {
    call.call.request(1)
    requestQ.enqueue1(message.some).unsafeRun()
  }

  override def onHalfClose(): Unit = requestQ.enqueue1(none).unsafeRun()

  override def source: Stream[F, Request] =
    requestQ.dequeue.unNoneTerminate
}

object Fs2StreamServerCallListener {
  class PartialFs2StreamServerCallListener[F[_]](val dummy: Boolean = false) extends AnyVal {

    def apply[Request, Response](
        call: ServerCall[Request, Response],
        options: ServerCallOptions = ServerCallOptions.default
    )(implicit
        F: ConcurrentEffect[F]
    ): F[Fs2StreamServerCallListener[F, Request, Response]] =
      for {
        inputQ <- Queue.unbounded[F, Option[Request]]
        isCancelled <- Deferred[F, Unit]
        serverCall <- Fs2ServerCall[F, Request, Response](call, options)
      } yield new Fs2StreamServerCallListener[F, Request, Response](inputQ, isCancelled, serverCall)
  }

  def apply[F[_]] = new PartialFs2StreamServerCallListener[F]

} 
Example 18
Source File: Fs2UnaryServerCallListener.scala    From fs2-grpc   with MIT License 5 votes vote down vote up
package org.lyranthe.fs2_grpc
package java_runtime
package server

import cats.effect.{ConcurrentEffect, Effect}
import cats.effect.concurrent.{Deferred, Ref}
import cats.syntax.all._
import io.grpc._

class Fs2UnaryServerCallListener[F[_], Request, Response] private (
    request: Ref[F, Option[Request]],
    isComplete: Deferred[F, Unit],
    val isCancelled: Deferred[F, Unit],
    val call: Fs2ServerCall[F, Request, Response]
)(implicit F: Effect[F])
    extends ServerCall.Listener[Request]
    with Fs2ServerCallListener[F, F, Request, Response] {

  import Fs2UnaryServerCallListener._

  override def onCancel(): Unit = {
    isCancelled.complete(()).unsafeRun()
  }

  override def onMessage(message: Request): Unit = {
    request.access
      .flatMap[Unit] {
        case (curValue, modify) =>
          if (curValue.isDefined)
            F.raiseError(statusException(TooManyRequests))
          else
            modify(message.some).void
      }
      .unsafeRun()

  }

  override def onHalfClose(): Unit =
    isComplete.complete(()).unsafeRun()

  override def source: F[Request] =
    for {
      _ <- isComplete.get
      valueOrNone <- request.get
      value <- valueOrNone.fold[F[Request]](F.raiseError(statusException(NoMessage)))(F.pure)
    } yield value
}

object Fs2UnaryServerCallListener {

  val TooManyRequests: String = "Too many requests"
  val NoMessage: String = "No message for unary call"

  private val statusException: String => StatusRuntimeException = msg =>
    new StatusRuntimeException(Status.INTERNAL.withDescription(msg))

  class PartialFs2UnaryServerCallListener[F[_]](val dummy: Boolean = false) extends AnyVal {

    def apply[Request, Response](
        call: ServerCall[Request, Response],
        options: ServerCallOptions = ServerCallOptions.default
    )(implicit
        F: ConcurrentEffect[F]
    ): F[Fs2UnaryServerCallListener[F, Request, Response]] =
      for {
        request <- Ref.of[F, Option[Request]](none)
        isComplete <- Deferred[F, Unit]
        isCancelled <- Deferred[F, Unit]
        serverCall <- Fs2ServerCall[F, Request, Response](call, options)
      } yield new Fs2UnaryServerCallListener[F, Request, Response](request, isComplete, isCancelled, serverCall)
  }

  def apply[F[_]] = new PartialFs2UnaryServerCallListener[F]
} 
Example 19
Source File: Publish.scala    From fs2-rabbit   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.fs2rabbit.algebra

import cats.effect.syntax.effect._
import cats.effect.{Blocker, ContextShift, Effect, Sync}
import cats.syntax.functor._
import com.rabbitmq.client.{AMQP, ReturnListener}
import dev.profunktor.fs2rabbit.model._

object Publish {
  def make[F[_]: Effect: ContextShift](
      blocker: Blocker
  ): Publish[F] =
    new Publish[F] {
      override def basicPublish(channel: AMQPChannel,
                                exchangeName: ExchangeName,
                                routingKey: RoutingKey,
                                msg: AmqpMessage[Array[Byte]]): F[Unit] =
        blocker.delay {
          channel.value.basicPublish(
            exchangeName.value,
            routingKey.value,
            msg.properties.asBasicProps,
            msg.payload
          )
        }

      override def basicPublishWithFlag(channel: AMQPChannel,
                                        exchangeName: ExchangeName,
                                        routingKey: RoutingKey,
                                        flag: PublishingFlag,
                                        msg: AmqpMessage[Array[Byte]]): F[Unit] =
        blocker.delay {
          channel.value.basicPublish(
            exchangeName.value,
            routingKey.value,
            flag.mandatory,
            msg.properties.asBasicProps,
            msg.payload
          )
        }

      override def addPublishingListener(
          channel: AMQPChannel,
          listener: PublishReturn => F[Unit]
      ): F[Unit] =
        Sync[F].delay {
          val returnListener = new ReturnListener {
            override def handleReturn(replyCode: Int,
                                      replyText: String,
                                      exchange: String,
                                      routingKey: String,
                                      properties: AMQP.BasicProperties,
                                      body: Array[Byte]): Unit = {
              val publishReturn =
                PublishReturn(
                  ReplyCode(replyCode),
                  ReplyText(replyText),
                  ExchangeName(exchange),
                  RoutingKey(routingKey),
                  AmqpProperties.unsafeFrom(properties),
                  AmqpBody(body)
                )

              listener(publishReturn).toIO.unsafeRunAsync(_ => ())
            }
          }

          channel.value.addReturnListener(returnListener)
        }.void

      override def clearPublishingListeners(channel: AMQPChannel): F[Unit] =
        Sync[F].delay {
          channel.value.clearReturnListeners()
        }.void
    }
}

trait Publish[F[_]] {
  def basicPublish(channel: AMQPChannel,
                   exchangeName: ExchangeName,
                   routingKey: RoutingKey,
                   msg: AmqpMessage[Array[Byte]]): F[Unit]
  def basicPublishWithFlag(channel: AMQPChannel,
                           exchangeName: ExchangeName,
                           routingKey: RoutingKey,
                           flag: PublishingFlag,
                           msg: AmqpMessage[Array[Byte]]): F[Unit]
  def addPublishingListener(channel: AMQPChannel, listener: PublishReturn => F[Unit]): F[Unit]
  def clearPublishingListeners(channel: AMQPChannel): F[Unit]
} 
Example 20
Source File: AckingProgram.scala    From fs2-rabbit   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.fs2rabbit.program

import cats.Applicative
import cats.effect.{Effect, Sync}
import dev.profunktor.fs2rabbit.algebra.{AMQPInternals, Acking, Consume}
import dev.profunktor.fs2rabbit.arguments.Arguments
import dev.profunktor.fs2rabbit.config.Fs2RabbitConfig
import dev.profunktor.fs2rabbit.model.AckResult.{Ack, NAck, Reject}
import dev.profunktor.fs2rabbit.model._

object AckingProgram {
  def make[F[_]: Effect](config: Fs2RabbitConfig): F[AckingProgram[F]] = Sync[F].delay {
    WrapperAckingProgram(config, Consume.make)
  }
}

trait AckingProgram[F[_]] extends Acking[F] with Consume[F]

case class WrapperAckingProgram[F[_]: Effect] private (
    config: Fs2RabbitConfig,
    consume: Consume[F]
) extends AckingProgram[F] {
  override def createAcker(channel: AMQPChannel): F[AckResult => F[Unit]] = Applicative[F].pure {
    case Ack(tag) => consume.basicAck(channel, tag, multiple = false)
    case NAck(tag) =>
      consume.basicNack(channel, tag, multiple = false, config.requeueOnNack)
    case Reject(tag) =>
      consume.basicReject(channel, tag, config.requeueOnReject)
  }

  override def basicAck(channel: AMQPChannel, tag: DeliveryTag, multiple: Boolean): F[Unit] =
    consume.basicAck(channel, tag, multiple)

  override def basicNack(channel: AMQPChannel, tag: DeliveryTag, multiple: Boolean, requeue: Boolean): F[Unit] =
    consume.basicNack(channel, tag, multiple, requeue)

  override def basicReject(channel: AMQPChannel, tag: DeliveryTag, requeue: Boolean): F[Unit] =
    consume.basicReject(channel, tag, requeue)

  override def basicQos(channel: AMQPChannel, basicQos: BasicQos): F[Unit] =
    consume.basicQos(channel, basicQos)

  override def basicConsume[A](channel: AMQPChannel,
                               queueName: QueueName,
                               autoAck: Boolean,
                               consumerTag: ConsumerTag,
                               noLocal: Boolean,
                               exclusive: Boolean,
                               args: Arguments)(internals: AMQPInternals[F]): F[ConsumerTag] =
    consume.basicConsume(channel, queueName, autoAck, consumerTag, noLocal, exclusive, args)(internals)

  override def basicCancel(channel: AMQPChannel, consumerTag: ConsumerTag): F[Unit] =
    consume.basicCancel(channel, consumerTag)
} 
Example 21
Source File: ConsumingProgram.scala    From fs2-rabbit   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.fs2rabbit.program

import cats.effect.{Effect, Sync}
import cats.implicits._
import dev.profunktor.fs2rabbit.algebra.ConsumingStream._
import dev.profunktor.fs2rabbit.algebra.{AMQPInternals, Consume, InternalQueue}
import dev.profunktor.fs2rabbit.arguments.Arguments
import dev.profunktor.fs2rabbit.effects.EnvelopeDecoder
import dev.profunktor.fs2rabbit.model._
import fs2.Stream

object ConsumingProgram {
  def make[F[_]: Effect](internalQueue: InternalQueue[F]): F[ConsumingProgram[F]] = Sync[F].delay {
    WrapperConsumingProgram(internalQueue, Consume.make)
  }
}

trait ConsumingProgram[F[_]] extends ConsumingStream[F] with Consume[F]

case class WrapperConsumingProgram[F[_]: Effect] private (
    internalQueue: InternalQueue[F],
    consume: Consume[F]
) extends ConsumingProgram[F] {

  override def createConsumer[A](
      queueName: QueueName,
      channel: AMQPChannel,
      basicQos: BasicQos,
      autoAck: Boolean = false,
      noLocal: Boolean = false,
      exclusive: Boolean = false,
      consumerTag: ConsumerTag = ConsumerTag(""),
      args: Arguments = Map.empty
  )(implicit decoder: EnvelopeDecoder[F, A]): F[Stream[F, AmqpEnvelope[A]]] = {

    val setup = for {
      internalQ <- internalQueue.create
      internals = AMQPInternals[F](Some(internalQ))
      _         <- consume.basicQos(channel, basicQos)
      consumerTag <- consume.basicConsume(
                      channel,
                      queueName,
                      autoAck,
                      consumerTag,
                      noLocal,
                      exclusive,
                      args
                    )(internals)
    } yield (consumerTag, internalQ)

    Stream
      .bracket(setup) {
        case (tag, _) =>
          consume.basicCancel(channel, tag)
      }
      .flatMap {
        case (_, queue) =>
          queue.dequeue.rethrow
            .evalMap(env => decoder(env).map(a => env.copy(payload = a)))
      }
      .pure[F]
  }

  override def basicAck(channel: AMQPChannel, tag: DeliveryTag, multiple: Boolean): F[Unit] =
    consume.basicAck(channel, tag, multiple)

  override def basicNack(channel: AMQPChannel, tag: DeliveryTag, multiple: Boolean, requeue: Boolean): F[Unit] =
    consume.basicNack(channel, tag, multiple, requeue)

  override def basicReject(channel: AMQPChannel, tag: DeliveryTag, requeue: Boolean): F[Unit] =
    consume.basicReject(channel, tag, requeue)

  override def basicQos(channel: AMQPChannel, basicQos: BasicQos): F[Unit] =
    consume.basicQos(channel, basicQos)

  override def basicConsume[A](
      channel: AMQPChannel,
      queueName: QueueName,
      autoAck: Boolean,
      consumerTag: ConsumerTag,
      noLocal: Boolean,
      exclusive: Boolean,
      args: Arguments
  )(internals: AMQPInternals[F]): F[ConsumerTag] =
    consume.basicConsume(channel, queueName, autoAck, consumerTag, noLocal, exclusive, args)(internals)

  override def basicCancel(channel: AMQPChannel, consumerTag: ConsumerTag): F[Unit] =
    consume.basicCancel(channel, consumerTag)
} 
Example 22
Source File: OTag.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.backend.ops

import cats.effect.{Effect, Resource}
import cats.implicits._

import docspell.common.{AccountId, Ident}
import docspell.store.records.{RTag, RTagItem}
import docspell.store.{AddResult, Store}

trait OTag[F[_]] {

  def findAll(account: AccountId, nameQuery: Option[String]): F[Vector[RTag]]

  def add(s: RTag): F[AddResult]

  def update(s: RTag): F[AddResult]

  def delete(id: Ident, collective: Ident): F[AddResult]

  
  def loadAll(ids: List[Ident]): F[Vector[RTag]]
}

object OTag {

  def apply[F[_]: Effect](store: Store[F]): Resource[F, OTag[F]] =
    Resource.pure[F, OTag[F]](new OTag[F] {
      def findAll(account: AccountId, nameQuery: Option[String]): F[Vector[RTag]] =
        store.transact(RTag.findAll(account.collective, nameQuery, _.name))

      def add(t: RTag): F[AddResult] = {
        def insert = RTag.insert(t)
        def exists = RTag.existsByName(t)

        val msg = s"A tag '${t.name}' already exists"
        store.add(insert, exists).map(_.fold(identity, _.withMsg(msg), identity))
      }

      def update(t: RTag): F[AddResult] = {
        def insert = RTag.update(t)
        def exists = RTag.existsByName(t)

        val msg = s"A tag '${t.name}' already exists"
        store.add(insert, exists).map(_.fold(identity, _.withMsg(msg), identity))
      }

      def delete(id: Ident, collective: Ident): F[AddResult] = {
        val io = for {
          optTag <- RTag.findByIdAndCollective(id, collective)
          n0     <- optTag.traverse(t => RTagItem.deleteTag(t.tagId))
          n1     <- optTag.traverse(t => RTag.delete(t.tagId, collective))
        } yield n0.getOrElse(0) + n1.getOrElse(0)
        store.transact(io).attempt.map(AddResult.fromUpdate)
      }

      def loadAll(ids: List[Ident]): F[Vector[RTag]] =
        if (ids.isEmpty) Vector.empty.pure[F]
        else store.transact(RTag.findAllById(ids))
    })
} 
Example 23
Source File: ONode.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.backend.ops

import cats.effect.{Effect, Resource}
import cats.implicits._

import docspell.common.syntax.all._
import docspell.common.{Ident, LenientUri, NodeType}
import docspell.store.Store
import docspell.store.records.RNode

import org.log4s._

trait ONode[F[_]] {

  def register(appId: Ident, nodeType: NodeType, uri: LenientUri): F[Unit]

  def unregister(appId: Ident): F[Unit]
}

object ONode {
  private[this] val logger = getLogger

  def apply[F[_]: Effect](store: Store[F]): Resource[F, ONode[F]] =
    Resource.pure[F, ONode[F]](new ONode[F] {

      def register(appId: Ident, nodeType: NodeType, uri: LenientUri): F[Unit] =
        for {
          node <- RNode(appId, nodeType, uri)
          _    <- logger.finfo(s"Registering node ${node.id.id}")
          _    <- store.transact(RNode.set(node))
        } yield ()

      def unregister(appId: Ident): F[Unit] =
        logger.finfo(s"Unregister app ${appId.id}") *>
          store.transact(RNode.delete(appId)).map(_ => ())
    })

} 
Example 24
Source File: OEquipment.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.backend.ops

import cats.effect.{Effect, Resource}
import cats.implicits._

import docspell.common.{AccountId, Ident}
import docspell.store.records.{REquipment, RItem}
import docspell.store.{AddResult, Store}

trait OEquipment[F[_]] {

  def findAll(account: AccountId, nameQuery: Option[String]): F[Vector[REquipment]]

  def add(s: REquipment): F[AddResult]

  def update(s: REquipment): F[AddResult]

  def delete(id: Ident, collective: Ident): F[AddResult]
}

object OEquipment {

  def apply[F[_]: Effect](store: Store[F]): Resource[F, OEquipment[F]] =
    Resource.pure[F, OEquipment[F]](new OEquipment[F] {
      def findAll(account: AccountId, nameQuery: Option[String]): F[Vector[REquipment]] =
        store.transact(REquipment.findAll(account.collective, nameQuery, _.name))

      def add(e: REquipment): F[AddResult] = {
        def insert = REquipment.insert(e)
        def exists = REquipment.existsByName(e.cid, e.name)

        val msg = s"An equipment '${e.name}' already exists"
        store.add(insert, exists).map(_.fold(identity, _.withMsg(msg), identity))
      }

      def update(e: REquipment): F[AddResult] = {
        def insert = REquipment.update(e)
        def exists = REquipment.existsByName(e.cid, e.name)

        val msg = s"An equipment '${e.name}' already exists"
        store.add(insert, exists).map(_.fold(identity, _.withMsg(msg), identity))
      }

      def delete(id: Ident, collective: Ident): F[AddResult] = {
        val io = for {
          n0 <- RItem.removeConcEquip(collective, id)
          n1 <- REquipment.delete(id, collective)
        } yield n0 + n1
        store.transact(io).attempt.map(AddResult.fromUpdate)
      }
    })
} 
Example 25
Source File: JobQueue.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.store.queue

import cats.effect.{Effect, Resource}
import cats.implicits._

import docspell.common._
import docspell.common.syntax.all._
import docspell.store.Store
import docspell.store.queries.QJob
import docspell.store.records.RJob

import org.log4s._

trait JobQueue[F[_]] {

  
  def insertIfNew(job: RJob): F[Unit]

  def insertAll(jobs: Seq[RJob]): F[Unit]

  def nextJob(
      prio: Ident => F[Priority],
      worker: Ident,
      retryPause: Duration
  ): F[Option[RJob]]
}

object JobQueue {
  private[this] val logger = getLogger

  def apply[F[_]: Effect](store: Store[F]): Resource[F, JobQueue[F]] =
    Resource.pure[F, JobQueue[F]](new JobQueue[F] {

      def nextJob(
          prio: Ident => F[Priority],
          worker: Ident,
          retryPause: Duration
      ): F[Option[RJob]] =
        logger
          .ftrace("Select next job") *> QJob.takeNextJob(store)(prio, worker, retryPause)

      def insert(job: RJob): F[Unit] =
        store
          .transact(RJob.insert(job))
          .flatMap { n =>
            if (n != 1)
              Effect[F]
                .raiseError(new Exception(s"Inserting job failed. Update count: $n"))
            else ().pure[F]
          }

      def insertIfNew(job: RJob): F[Unit] =
        for {
          rj <- job.tracker match {
            case Some(tid) =>
              store.transact(RJob.findNonFinalByTracker(tid))
            case None =>
              None.pure[F]
          }
          ret <-
            if (rj.isDefined) ().pure[F]
            else insert(job)
        } yield ret

      def insertAll(jobs: Seq[RJob]): F[Unit] =
        jobs.toList
          .traverse(j => insert(j).attempt)
          .map(_.foreach {
            case Right(()) =>
            case Left(ex) =>
              logger.error(ex)("Could not insert job. Skipping it.")
          })

    })
} 
Example 26
Source File: StoreImpl.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.store.impl

import cats.effect.Effect
import cats.implicits._

import docspell.common.Ident
import docspell.store.migrate.FlywayMigrate
import docspell.store.{AddResult, JdbcConfig, Store}

import bitpeace.{Bitpeace, BitpeaceConfig, TikaMimetypeDetect}
import doobie._
import doobie.implicits._

final class StoreImpl[F[_]: Effect](jdbc: JdbcConfig, xa: Transactor[F])
    extends Store[F] {
  val bitpeaceCfg =
    BitpeaceConfig(
      "filemeta",
      "filechunk",
      TikaMimetypeDetect,
      Ident.randomId[F].map(_.id)
    )

  def migrate: F[Int] =
    FlywayMigrate.run[F](jdbc)

  def transact[A](prg: doobie.ConnectionIO[A]): F[A] =
    prg.transact(xa)

  def transact[A](prg: fs2.Stream[doobie.ConnectionIO, A]): fs2.Stream[F, A] =
    prg.transact(xa)

  def bitpeace: Bitpeace[F] =
    Bitpeace(bitpeaceCfg, xa)

  def add(insert: ConnectionIO[Int], exists: ConnectionIO[Boolean]): F[AddResult] =
    for {
      save  <- transact(insert).attempt
      exist <- save.swap.traverse(ex => transact(exists).map(b => (ex, b)))
    } yield exist.swap match {
      case Right(_) => AddResult.Success
      case Left((_, true)) =>
        AddResult.EntityExists("Adding failed, because the entity already exists.")
      case Left((ex, _)) => AddResult.Failure(ex)
    }
} 
Example 27
Source File: JsonLogEncodingTest.scala    From cedi-dtrace   with Apache License 2.0 5 votes vote down vote up
package com.ccadllc.cedi.dtrace
package logging

import cats.effect.{ IO, Effect }

import io.circe._
import io.circe.syntax._

import org.scalacheck.Arbitrary

import org.scalatest.wordspec.AnyWordSpec

import json.encoding._

class JsonLogEncodingTests extends AnyWordSpec with TestSupport {

  // format: OFF
  val calculateQuarterlySalesTraceContextJson = Json.obj(
    "where"           -> calculateQuarterlySalesTraceContext.system.data.allValues.asJson,
    "root"            -> calculateQuarterlySalesTraceContext.currentSpan.root.asJson,
    "trace-id"        -> calculateQuarterlySalesTraceContext.currentSpan.spanId.traceId.asJson,
    "span-id"         -> calculateQuarterlySalesTraceContext.currentSpan.spanId.spanId.asJson,
    "parent-id"       -> calculateQuarterlySalesTraceContext.currentSpan.spanId.parentSpanId.asJson,
    "span-name"       -> calculateQuarterlySalesTraceContext.currentSpan.spanName.value.asJson,
    "start-time"      -> calculateQuarterlySalesTraceContext.currentSpan.startTime.asJson,
    "span-success"    -> calculateQuarterlySalesTraceContext.currentSpan.failure.isEmpty.asJson,
    "failure-detail"  -> calculateQuarterlySalesTraceContext.currentSpan.failure.map(_.render).asJson,
    "span-duration"   -> calculateQuarterlySalesTraceContext.currentSpan.duration.toMicros.asJson,
    "notes"           -> Map(
      quarterlySalesUnitsNote.name.value        ->  quarterlySalesUnitsNoteValue.value.toString,
      quarterlySalesGoalReachedNote.name.value  ->  quarterlySalesGoalReachedNoteValue.value.toString,
      salesRegionNote.name.value                ->  salesRegionNoteValue.value,
      quarterlySalesTotalNote.name.value        ->  quarterlySalesTotalNoteValue.value.toString
    ).asJson
  )
  // format: ON

  implicit def traceArb[F[_]: Effect]: Arbitrary[TraceContext[F]] = Arbitrary(genTraceContext[F])

  "Trace" should { encodeGeneratedJson[TraceContext[IO]] }
  "Trace" should { encodeSpecificJson(calculateQuarterlySalesTraceContext, calculateQuarterlySalesTraceContextJson) }
} 
Example 28
Source File: AkkaActorIntermediator.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.sourcing.akka

import akka.actor.ActorSystem
import akka.pattern.ask
import akka.util.Timeout
import cats.effect.{ContextShift, Effect, IO, Timer}
import cats.syntax.all._
import ch.epfl.bluebrain.nexus.sourcing.akka.Msg._
import retry.CatsEffect._
import retry.syntax.all._
import retry.{RetryDetails, RetryPolicy}

import scala.reflect.ClassTag


abstract private[akka] class AkkaActorIntermediator[F[_]: Timer](
    name: String,
    selection: ActorRefSelection[F],
    askTimeout: Timeout
)(implicit F: Effect[F], as: ActorSystem, policy: RetryPolicy[F]) {

  implicit private[akka] val contextShift: ContextShift[IO]        = IO.contextShift(as.dispatcher)
  implicit private[akka] def noop[A]: (A, RetryDetails) => F[Unit] = retry.noop[F, A]
  implicit private val timeout: Timeout                            = askTimeout

  private[akka] def send[M <: Msg, Reply, A](id: String, msg: M, f: Reply => A)(implicit
      Reply: ClassTag[Reply]
  ): F[A] =
    selection(name, id).flatMap { ref =>
      val future = IO(ref ? msg)
      val fa     = IO.fromFuture(future).to[F]
      fa.flatMap[A] {
          case Reply(value)                     => F.pure(f(value))
          case te: TypeError                    => F.raiseError(te)
          case um: UnexpectedMsgId              => F.raiseError(um)
          case cet: CommandEvaluationTimeout[_] => F.raiseError(cet)
          case cee: CommandEvaluationError[_]   => F.raiseError(cee)
          case other                            => F.raiseError(TypeError(id, Reply.runtimeClass.getSimpleName, other))
        }
        .retryingOnAllErrors[Throwable]
    }
} 
Example 29
Source File: ElasticSearchBaseClient.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.commons.es.client

import akka.http.scaladsl.model.StatusCodes.GatewayTimeout
import akka.http.scaladsl.model.{HttpRequest, StatusCode, StatusCodes}
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.commons.es.client.ElasticSearchBaseClient._
import ch.epfl.bluebrain.nexus.commons.es.client.ElasticSearchFailure.{ElasticServerError, ElasticUnexpectedError}
import ch.epfl.bluebrain.nexus.commons.http.HttpClient.UntypedHttpClient
import ch.epfl.bluebrain.nexus.sourcing.RetryStrategyConfig
import com.typesafe.scalalogging.Logger
import retry.CatsEffect._
import retry.syntax.all._
import retry.{RetryDetails, RetryPolicy}

import scala.util.control.NonFatal


  private[client] def sanitize(index: String, allowWildCard: Boolean): String = {
    val regex = if (allowWildCard) """[\s|"|\\|<|>|\||,|/|?]""" else """[\s|"|*|\\|<|>|\||,|/|?]"""
    index.replaceAll(regex, "_").dropWhile(_ == '_')
  }
}

object ElasticSearchBaseClient {
  private[client] val docType           = "_doc"
  private[client] val source            = "_source"
  private[client] val anyIndexPath      = "_all"
  private[client] val ignoreUnavailable = "ignore_unavailable"
  private[client] val allowNoIndices    = "allow_no_indices"
  private[client] val trackTotalHits    = "track_total_hits"
} 
Example 30
Source File: ArchiveCache.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.archives

import akka.actor.{ActorSystem, NotInfluenceReceiveTimeout}
import cats.Monad
import cats.data.OptionT
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.kg.archives.ArchiveCache._
import ch.epfl.bluebrain.nexus.kg.config.KgConfig.ArchivesConfig
import ch.epfl.bluebrain.nexus.kg.resources.ResId
import ch.epfl.bluebrain.nexus.sourcing.StateMachine
import ch.epfl.bluebrain.nexus.sourcing.akka.StopStrategy
import ch.epfl.bluebrain.nexus.sourcing.akka.statemachine.AkkaStateMachine
import retry.RetryPolicy

class ArchiveCache[F[_]: Monad](ref: StateMachine[F, String, State, Command, Unit]) {

  
  def put(value: Archive): OptionT[F, Archive] =
    OptionT(ref.evaluate(value.resId.show, Write(value)).map(_.toOption.flatten))

}

object ArchiveCache {

  private[archives] type State   = Option[Archive]
  private[archives] type Command = Write
  final private[archives] case class Write(bundle: Archive) extends NotInfluenceReceiveTimeout

  final def apply[F[_]: Timer](implicit as: ActorSystem, cfg: ArchivesConfig, F: Effect[F]): F[ArchiveCache[F]] = {
    implicit val retryPolicy: RetryPolicy[F] = cfg.cache.retry.retryPolicy[F]
    val invalidationStrategy                 = StopStrategy.lapsedSinceLastInteraction[State, Command](cfg.cacheInvalidateAfter)

    val evaluate: (State, Command) => F[Either[Unit, State]] = {
      case (None, Write(bundle)) => F.pure(Right(Some(bundle)))
      case (Some(_), _)          => F.pure(Left(())) // It already exists, so we don't want to replace it
    }

    AkkaStateMachine
      .sharded[F]("archives", None, evaluate, invalidationStrategy, cfg.cache.akkaStateMachineConfig, cfg.cache.shards)
      .map(new ArchiveCache[F](_))
  }
} 
Example 31
Source File: ElasticSearchIndexer.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.indexing

import akka.actor.{ActorRef, ActorSystem, Props}
import akka.stream.scaladsl.Source
import akka.util.Timeout
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.client.types.Project
import ch.epfl.bluebrain.nexus.commons.es.client.ElasticSearchClient
import ch.epfl.bluebrain.nexus.commons.es.client.ElasticSearchClient.BulkOp
import ch.epfl.bluebrain.nexus.kg.indexing.View.ElasticSearchView
import ch.epfl.bluebrain.nexus.kg.resources._
import ch.epfl.bluebrain.nexus.kg.routes.Clients
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig
import ch.epfl.bluebrain.nexus.sourcing.projections.ProgressFlow.ProgressFlowElem
import ch.epfl.bluebrain.nexus.sourcing.projections.ProjectionProgress.NoProgress
import ch.epfl.bluebrain.nexus.sourcing.projections._
import com.typesafe.scalalogging.Logger

import scala.concurrent.ExecutionContext

// $COVERAGE-OFF$
@SuppressWarnings(Array("MaxParameters"))
object ElasticSearchIndexer {

  implicit private val log: Logger = Logger[ElasticSearchIndexer.type]

  
  final def start[F[_]: Timer](
      view: ElasticSearchView,
      resources: Resources[F],
      project: Project,
      restartOffset: Boolean
  )(implicit
      as: ActorSystem,
      actorInitializer: (Props, String) => ActorRef,
      projections: Projections[F, String],
      F: Effect[F],
      clients: Clients[F],
      config: ServiceConfig
  ): StreamSupervisor[F, ProjectionProgress] = {

    implicit val ec: ExecutionContext          = as.dispatcher
    implicit val p: Project                    = project
    implicit val indexing: IndexingConfig      = config.kg.elasticSearch.indexing
    implicit val metadataOpts: MetadataOptions = MetadataOptions(linksAsIri = true, expandedLinks = true)
    implicit val tm: Timeout                   = Timeout(config.kg.elasticSearch.askTimeout)

    val client: ElasticSearchClient[F] = clients.elasticSearch.withRetryPolicy(config.kg.elasticSearch.indexing.retry)

    def deleteOrIndex(res: ResourceV): Option[BulkOp] =
      if (res.deprecated && !view.filter.includeDeprecated) Some(delete(res))
      else view.toDocument(res).map(doc => BulkOp.Index(view.index, res.id.value.asString, doc))

    def delete(res: ResourceV): BulkOp =
      BulkOp.Delete(view.index, res.id.value.asString)

    val initFetchProgressF: F[ProjectionProgress] =
      if (restartOffset)
        projections.recordProgress(view.progressId, NoProgress) >> view.createIndex >> F.pure(NoProgress)
      else view.createIndex >> projections.progress(view.progressId)

    val sourceF: F[Source[ProjectionProgress, _]] = initFetchProgressF.map { initial =>
      val flow = ProgressFlowElem[F, Any]
        .collectCast[Event]
        .groupedWithin(indexing.batch, indexing.batchTimeout)
        .distinct()
        .mapAsync(view.toResource(resources, _))
        .collectSome[ResourceV]
        .collect {
          case res if view.allowedSchemas(res) && view.allowedTypes(res) => deleteOrIndex(res)
          case res if view.allowedSchemas(res)                           => Some(delete(res))
        }
        .collectSome[BulkOp]
        .runAsyncBatch(client.bulk(_))()
        .mergeEmit()
        .toPersistedProgress(view.progressId, initial)

      cassandraSource(s"project=${view.ref.id}", view.progressId, initial.minProgress.offset)
        .via(flow)
        .via(kamonViewMetricsFlow(view, project))
    }
    StreamSupervisor.start(sourceF, view.progressId, actorInitializer)
  }
}
// $COVERAGE-ON$ 
Example 32
Source File: StorageIndexer.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.indexing

import java.time.Instant

import akka.actor.ActorSystem
import akka.stream.scaladsl.{Flow, Source}
import akka.util.Timeout
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.client.AdminClient
import ch.epfl.bluebrain.nexus.iam.auth.AccessToken
import ch.epfl.bluebrain.nexus.kg.cache.{ProjectCache, StorageCache}
import ch.epfl.bluebrain.nexus.kg.config.KgConfig.StorageConfig
import ch.epfl.bluebrain.nexus.kg.resources._
import ch.epfl.bluebrain.nexus.kg.storage.Storage
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig
import ch.epfl.bluebrain.nexus.service.config.Vocabulary.nxv
import ch.epfl.bluebrain.nexus.sourcing.projections.ProgressFlow.{PairMsg, ProgressFlowElem}
import ch.epfl.bluebrain.nexus.sourcing.projections._
import com.typesafe.scalalogging.Logger

import scala.concurrent.ExecutionContext

// $COVERAGE-OFF$
object StorageIndexer {

  implicit private val log = Logger[StorageIndexer.type]

  def start[F[_]: Timer](storages: Storages[F], storageCache: StorageCache[F])(implicit
      projectCache: ProjectCache[F],
      F: Effect[F],
      as: ActorSystem,
      projectInitializer: ProjectInitializer[F],
      adminClient: AdminClient[F],
      config: ServiceConfig
  ): StreamSupervisor[F, Unit] = {

    implicit val authToken: Option[AccessToken] = config.serviceAccount.credentials
    implicit val indexing: IndexingConfig       = config.kg.keyValueStore.indexing
    implicit val ec: ExecutionContext           = as.dispatcher
    implicit val tm: Timeout                    = Timeout(config.kg.keyValueStore.askTimeout)
    implicit val storageConfig: StorageConfig   = config.kg.storage
    val name                                    = "storage-indexer"

    def toStorage(event: Event): F[Option[(Storage, Instant)]] =
      fetchProject(event.organization, event.id.parent, event.subject).flatMap { implicit project =>
        storages.fetchStorage(event.id).value.map {
          case Left(err)           =>
            log.error(s"Error on event '${event.id.show} (rev = ${event.rev})', cause: '${err.msg}'")
            None
          case Right(timedStorage) => Some(timedStorage)
        }
      }

    val source: Source[PairMsg[Any], _]   = cassandraSource(s"type=${nxv.Storage.value.show}", name)
    val flow: Flow[PairMsg[Any], Unit, _] = ProgressFlowElem[F, Any]
      .collectCast[Event]
      .groupedWithin(indexing.batch, indexing.batchTimeout)
      .distinct()
      .mergeEmit()
      .mapAsync(toStorage)
      .collectSome[(Storage, Instant)]
      .runAsync { case (storage, instant) => storageCache.put(storage)(instant) }()
      .flow
      .map(_ => ())

    StreamSupervisor.startSingleton(F.delay(source.via(flow)), name)
  }
}
// $COVERAGE-ON$ 
Example 33
Source File: SparqlIndexer.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.indexing

import akka.actor.{ActorRef, ActorSystem, Props}
import akka.stream.scaladsl.Source
import akka.util.Timeout
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.client.types.Project
import ch.epfl.bluebrain.nexus.commons.sparql.client.{BlazegraphClient, SparqlWriteQuery}
import ch.epfl.bluebrain.nexus.kg.indexing.View.SparqlView
import ch.epfl.bluebrain.nexus.kg.resources._
import ch.epfl.bluebrain.nexus.kg.routes.Clients
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig
import ch.epfl.bluebrain.nexus.sourcing.projections.ProgressFlow.ProgressFlowElem
import ch.epfl.bluebrain.nexus.sourcing.projections.ProjectionProgress.NoProgress
import ch.epfl.bluebrain.nexus.sourcing.projections._

import scala.concurrent.ExecutionContext

// $COVERAGE-OFF$
@SuppressWarnings(Array("MaxParameters"))
object SparqlIndexer {

  
  final def start[F[_]: Timer](
      view: SparqlView,
      resources: Resources[F],
      project: Project,
      restartOffset: Boolean
  )(implicit
      as: ActorSystem,
      actorInitializer: (Props, String) => ActorRef,
      projections: Projections[F, String],
      F: Effect[F],
      clients: Clients[F],
      config: ServiceConfig
  ): StreamSupervisor[F, ProjectionProgress] = {

    implicit val ec: ExecutionContext          = as.dispatcher
    implicit val p: Project                    = project
    implicit val indexing: IndexingConfig      = config.kg.sparql.indexing
    implicit val metadataOpts: MetadataOptions = MetadataOptions(linksAsIri = true, expandedLinks = true)
    implicit val tm: Timeout                   = Timeout(config.kg.sparql.askTimeout)

    val client: BlazegraphClient[F] =
      clients.sparql.copy(namespace = view.index).withRetryPolicy(config.kg.sparql.indexing.retry)

    def buildInsertOrDeleteQuery(res: ResourceV): SparqlWriteQuery =
      if (res.deprecated && !view.filter.includeDeprecated) view.buildDeleteQuery(res)
      else view.buildInsertQuery(res)

    val initFetchProgressF: F[ProjectionProgress] =
      if (restartOffset)
        projections.recordProgress(view.progressId, NoProgress) >> view.createIndex >> F.pure(NoProgress)
      else view.createIndex >> projections.progress(view.progressId)

    val sourceF: F[Source[ProjectionProgress, _]] = initFetchProgressF.map { initial =>
      val flow = ProgressFlowElem[F, Any]
        .collectCast[Event]
        .groupedWithin(indexing.batch, indexing.batchTimeout)
        .distinct()
        .mapAsync(view.toResource(resources, _))
        .collectSome[ResourceV]
        .collect {
          case res if view.allowedSchemas(res) && view.allowedTypes(res) => buildInsertOrDeleteQuery(res)
          case res if view.allowedSchemas(res)                           => view.buildDeleteQuery(res)
        }
        .runAsyncBatch(client.bulk(_))()
        .mergeEmit()
        .toPersistedProgress(view.progressId, initial)
      cassandraSource(s"project=${view.ref.id}", view.progressId, initial.minProgress.offset)
        .via(flow)
        .via(kamonViewMetricsFlow(view, project))
    }
    StreamSupervisor.start(sourceF, view.progressId, actorInitializer)
  }
}
// $COVERAGE-ON$ 
Example 34
Source File: ResolverIndexer.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.indexing

import akka.actor.ActorSystem
import akka.stream.scaladsl.{Flow, Source}
import akka.util.Timeout
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.client.AdminClient
import ch.epfl.bluebrain.nexus.iam.auth.AccessToken
import ch.epfl.bluebrain.nexus.kg.cache.{ProjectCache, ResolverCache}
import ch.epfl.bluebrain.nexus.kg.resolve.Resolver
import ch.epfl.bluebrain.nexus.kg.resources._
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig
import ch.epfl.bluebrain.nexus.service.config.Vocabulary.nxv
import ch.epfl.bluebrain.nexus.sourcing.projections.ProgressFlow.{PairMsg, ProgressFlowElem}
import ch.epfl.bluebrain.nexus.sourcing.projections._
import com.typesafe.scalalogging.Logger

import scala.concurrent.ExecutionContext

// $COVERAGE-OFF$
object ResolverIndexer {

  implicit private val log = Logger[ResolverIndexer.type]

  
  final def start[F[_]: Timer](resolvers: Resolvers[F], resolverCache: ResolverCache[F])(implicit
      projectCache: ProjectCache[F],
      as: ActorSystem,
      F: Effect[F],
      projectInitializer: ProjectInitializer[F],
      adminClient: AdminClient[F],
      config: ServiceConfig
  ): StreamSupervisor[F, Unit] = {
    implicit val authToken: Option[AccessToken] = config.serviceAccount.credentials
    implicit val indexing: IndexingConfig       = config.kg.keyValueStore.indexing
    implicit val ec: ExecutionContext           = as.dispatcher
    implicit val tm: Timeout                    = Timeout(config.kg.keyValueStore.askTimeout)

    val name = "resolver-indexer"

    def toResolver(event: Event): F[Option[Resolver]] =
      fetchProject(event.organization, event.id.parent, event.subject).flatMap { implicit project =>
        resolvers.fetchResolver(event.id).value.map {
          case Left(err)       =>
            log.error(s"Error on event '${event.id.show} (rev = ${event.rev})', cause: '${err.msg}'")
            None
          case Right(resolver) => Some(resolver)
        }
      }

    val source: Source[PairMsg[Any], _]   = cassandraSource(s"type=${nxv.Resolver.value.show}", name)
    val flow: Flow[PairMsg[Any], Unit, _] = ProgressFlowElem[F, Any]
      .collectCast[Event]
      .groupedWithin(indexing.batch, indexing.batchTimeout)
      .distinct()
      .mergeEmit()
      .mapAsync(toResolver)
      .collectSome[Resolver]
      .runAsync(resolverCache.put)()
      .flow
      .map(_ => ())

    StreamSupervisor.startSingleton(F.delay(source.via(flow)), name)
  }
}
// $COVERAGE-ON$ 
Example 35
Source File: ViewIndexer.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.indexing

import akka.actor.ActorSystem
import akka.stream.scaladsl.{Flow, Source}
import akka.util.Timeout
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.client.AdminClient
import ch.epfl.bluebrain.nexus.iam.auth.AccessToken
import ch.epfl.bluebrain.nexus.kg.cache.{ProjectCache, ViewCache}
import ch.epfl.bluebrain.nexus.kg.resources._
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig
import ch.epfl.bluebrain.nexus.service.config.Vocabulary.nxv
import ch.epfl.bluebrain.nexus.sourcing.projections.ProgressFlow.{PairMsg, ProgressFlowElem}
import ch.epfl.bluebrain.nexus.sourcing.projections._
import com.typesafe.scalalogging.Logger

import scala.concurrent.ExecutionContext

// $COVERAGE-OFF$
object ViewIndexer {

  implicit private val log = Logger[ViewIndexer.type]

  def start[F[_]: Timer](views: Views[F], viewCache: ViewCache[F])(implicit
      projectCache: ProjectCache[F],
      F: Effect[F],
      as: ActorSystem,
      projectInitializer: ProjectInitializer[F],
      adminClient: AdminClient[F],
      config: ServiceConfig
  ): StreamSupervisor[F, Unit] = {

    implicit val authToken: Option[AccessToken] = config.serviceAccount.credentials
    implicit val indexing: IndexingConfig       = config.kg.keyValueStore.indexing
    implicit val ec: ExecutionContext           = as.dispatcher
    implicit val tm: Timeout                    = Timeout(config.kg.keyValueStore.askTimeout)
    val name                                    = "view-indexer"

    def toView(event: Event): F[Option[View]] =
      fetchProject(event.organization, event.id.parent, event.subject).flatMap { implicit project =>
        views.fetchView(event.id).value.map {
          case Left(err)   =>
            log.error(s"Error on event '${event.id.show} (rev = ${event.rev})', cause: '${err.msg}'")
            None
          case Right(view) => Some(view)
        }
      }

    val source: Source[PairMsg[Any], _]   = cassandraSource(s"type=${nxv.View.value.show}", name)
    val flow: Flow[PairMsg[Any], Unit, _] = ProgressFlowElem[F, Any]
      .collectCast[Event]
      .groupedWithin(indexing.batch, indexing.batchTimeout)
      .distinct()
      .mergeEmit()
      .mapAsync(toView)
      .collectSome[View]
      .runAsync(viewCache.put)()
      .flow
      .map(_ => ())

    StreamSupervisor.startSingleton(F.delay(source.via(flow)), name)
  }
}
// $COVERAGE-ON$ 
Example 36
Source File: ResolverCache.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.cache

import java.util.UUID
import java.util.concurrent.ConcurrentHashMap

import akka.actor.ActorSystem
import cats.Monad
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.commons.cache.{KeyValueStore, KeyValueStoreConfig}
import ch.epfl.bluebrain.nexus.kg.cache.Cache._
import ch.epfl.bluebrain.nexus.kg.resolve.Resolver
import ch.epfl.bluebrain.nexus.kg.resources.ProjectIdentifier.ProjectRef
import ch.epfl.bluebrain.nexus.rdf.Iri.AbsoluteIri

class ResolverCache[F[_]: Effect: Timer] private (projectToCache: ConcurrentHashMap[UUID, ResolverProjectCache[F]])(
    implicit
    as: ActorSystem,
    config: KeyValueStoreConfig
) {

  
private class ResolverProjectCache[F[_]: Monad] private (store: KeyValueStore[F, AbsoluteIri, Resolver])
    extends Cache[F, AbsoluteIri, Resolver](store) {

  implicit private val ordering: Ordering[Resolver] = Ordering.by(_.priority)

  def get: F[List[Resolver]] = store.values.map(_.toList.sorted)

  def put(resolver: Resolver): F[Unit] =
    if (resolver.deprecated) store.remove(resolver.id)
    else store.put(resolver.id, resolver)

}

private object ResolverProjectCache {

  def apply[F[_]: Effect: Timer](
      project: ProjectRef
  )(implicit as: ActorSystem, config: KeyValueStoreConfig): ResolverProjectCache[F] =
    new ResolverProjectCache(KeyValueStore.distributed(s"resolver-${project.id}", (_, resolver) => resolver.rev))

}

object ResolverCache {

  def apply[F[_]: Effect: Timer](implicit as: ActorSystem, config: KeyValueStoreConfig): ResolverCache[F] =
    new ResolverCache(new ConcurrentHashMap[UUID, ResolverProjectCache[F]]())
} 
Example 37
Source File: StorageCache.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.cache

import java.time.{Clock, Instant}
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap

import akka.actor.ActorSystem
import cats.Monad
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.commons.cache.{KeyValueStore, KeyValueStoreConfig}
import ch.epfl.bluebrain.nexus.kg.RevisionedValue
import ch.epfl.bluebrain.nexus.kg.cache.Cache._
import ch.epfl.bluebrain.nexus.kg.cache.StorageProjectCache._
import ch.epfl.bluebrain.nexus.kg.resources.ProjectIdentifier.ProjectRef
import ch.epfl.bluebrain.nexus.kg.storage.Storage
import ch.epfl.bluebrain.nexus.rdf.Iri.AbsoluteIri

class StorageCache[F[_]: Effect: Timer] private (projectToCache: ConcurrentHashMap[UUID, StorageProjectCache[F]])(
    implicit
    as: ActorSystem,
    config: KeyValueStoreConfig,
    clock: Clock
) {

  
private class StorageProjectCache[F[_]: Monad] private (store: KeyValueStore[F, AbsoluteIri, RevisionedStorage])
    extends Cache[F, AbsoluteIri, RevisionedStorage](store) {

  implicit private val ordering: Ordering[RevisionedStorage] = Ordering.by((s: RevisionedStorage) => s.rev).reverse

  implicit private def revisioned(storage: Storage)(implicit instant: Instant): RevisionedStorage =
    RevisionedValue(instant.toEpochMilli, storage)

  def get: F[List[Storage]] =
    store.values.map(_.toList.sorted.map(_.value))

  def getDefault: F[Option[Storage]]                            =
    get.map(_.collectFirst { case storage if storage.default => storage })

  def getBy(id: AbsoluteIri): F[Option[Storage]]                =
    get(id).map(_.collectFirst { case RevisionedValue(_, storage) if storage.id == id => storage })

  def put(storage: Storage)(implicit instant: Instant): F[Unit] =
    if (storage.deprecated) store.remove(storage.id)
    else store.put(storage.id, storage)
}

private object StorageProjectCache {

  type RevisionedStorage = RevisionedValue[Storage]

  def apply[F[_]: Effect: Timer](
      project: ProjectRef
  )(implicit as: ActorSystem, config: KeyValueStoreConfig): StorageProjectCache[F] =
    new StorageProjectCache(
      KeyValueStore.distributed(s"storage-${project.id}", (_, storage) => storage.value.rev)
    )

}

object StorageCache {

  def apply[F[_]: Timer: Effect](implicit as: ActorSystem, config: KeyValueStoreConfig, clock: Clock): StorageCache[F] =
    new StorageCache(new ConcurrentHashMap[UUID, StorageProjectCache[F]]())
} 
Example 38
Source File: RemoteDiskStorageOperations.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.storage

import akka.http.scaladsl.model.Uri
import cats.Applicative
import cats.effect.Effect
import cats.implicits._
import ch.epfl.bluebrain.nexus.iam.auth.AccessToken
import ch.epfl.bluebrain.nexus.iam.client.types.AuthToken
import ch.epfl.bluebrain.nexus.kg.resources.ResId
import ch.epfl.bluebrain.nexus.kg.resources.file.File._
import ch.epfl.bluebrain.nexus.kg.storage.Storage._
import ch.epfl.bluebrain.nexus.storage.client.StorageClient
import ch.epfl.bluebrain.nexus.storage.client.types.FileAttributes.{Digest => StorageDigest}
import ch.epfl.bluebrain.nexus.storage.client.types.{FileAttributes => StorageFileAttributes}

object RemoteDiskStorageOperations {

  // TODO: Remove when migrating ADMIN client
  implicit private def oldTokenConversion(implicit token: Option[AccessToken]): Option[AuthToken] =
    token.map(t => AuthToken(t.value))

  implicit private def toDigest(digest: StorageDigest): Digest = Digest(digest.algorithm, digest.value)

  
  final class FetchAttributes[F[_]](storage: RemoteDiskStorage, client: StorageClient[F])
      extends FetchFileAttributes[F] {
    implicit val cred = storage.credentials.map(AccessToken)

    override def apply(relativePath: Uri.Path): F[StorageFileAttributes] =
      client.getAttributes(storage.folder, relativePath)
  }

} 
Example 39
Source File: Main.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.storage

import java.nio.file.Paths
import java.time.Clock

import akka.actor.ActorSystem
import akka.event.{Logging, LoggingAdapter}
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Route
import akka.util.Timeout
import cats.effect.Effect
import ch.epfl.bluebrain.nexus.storage.Storages.DiskStorage
import ch.epfl.bluebrain.nexus.storage.attributes.AttributesCache
import ch.epfl.bluebrain.nexus.storage.config.{AppConfig, Settings}
import ch.epfl.bluebrain.nexus.storage.config.AppConfig._
import ch.epfl.bluebrain.nexus.storage.routes.Routes
import com.typesafe.config.{Config, ConfigFactory}
import kamon.Kamon
import monix.eval.Task
import monix.execution.Scheduler

import scala.concurrent.duration._
import scala.concurrent.{Await, ExecutionContext, Future}
import scala.util.{Failure, Success}

//noinspection TypeAnnotation
// $COVERAGE-OFF$
object Main {

  def loadConfig(): Config = {
    val cfg = sys.env.get("STORAGE_CONFIG_FILE") orElse sys.props.get("storage.config.file") map { str =>
      val file = Paths.get(str).toAbsolutePath.toFile
      ConfigFactory.parseFile(file)
    } getOrElse ConfigFactory.empty()
    (cfg withFallback ConfigFactory.load()).resolve()
  }

  def setupMonitoring(config: Config): Unit = {
    if (sys.env.getOrElse("KAMON_ENABLED", "false").toBoolean) {
      Kamon.reconfigure(config)
      Kamon.loadModules()
    }
  }

  def shutdownMonitoring(): Unit = {
    if (sys.env.getOrElse("KAMON_ENABLED", "false").toBoolean) {
      Await.result(Kamon.stopModules(), 10.seconds)
    }
  }

  @SuppressWarnings(Array("UnusedMethodParameter"))
  def main(args: Array[String]): Unit = {
    val config = loadConfig()
    setupMonitoring(config)

    implicit val appConfig: AppConfig = Settings(config).appConfig

    implicit val as: ActorSystem                          = ActorSystem(appConfig.description.fullName, config)
    implicit val ec: ExecutionContext                     = as.dispatcher
    implicit val eff: Effect[Task]                        = Task.catsEffect(Scheduler.global)
    implicit val iamIdentities: IamIdentitiesClient[Task] = new IamIdentitiesClient[Task](appConfig.iam)
    implicit val timeout                                  = Timeout(1.minute)
    implicit val clock                                    = Clock.systemUTC

    val storages: Storages[Task, AkkaSource] =
      new DiskStorage(appConfig.storage, appConfig.digest, AttributesCache[Task, AkkaSource])

    val logger: LoggingAdapter = Logging(as, getClass)

    logger.info("==== Cluster is Live ====")
    val routes: Route = Routes(storages)

    val httpBinding: Future[Http.ServerBinding] = {
      Http().bindAndHandle(routes, appConfig.http.interface, appConfig.http.port)
    }
    httpBinding onComplete {
      case Success(binding) =>
        logger.info(s"Bound to ${binding.localAddress.getHostString}: ${binding.localAddress.getPort}")
      case Failure(th)      =>
        logger.error(th, "Failed to perform an http binding on {}:{}", appConfig.http.interface, appConfig.http.port)
        Await.result(as.terminate(), 10.seconds)
    }

    as.registerOnTermination {
      shutdownMonitoring()
    }
    // attempt to leave the cluster before shutting down
    val _ = sys.addShutdownHook {
      Await.result(as.terminate().map(_ => ()), 10.seconds)
    }
  }
}
// $COVERAGE-ON$ 
Example 40
Source File: AttributesCache.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.storage.attributes

import java.nio.file.Path
import java.time.Clock

import akka.actor.{ActorRef, ActorSystem}
import akka.pattern.{ask, AskTimeoutException}
import akka.util.Timeout
import cats.effect.{ContextShift, Effect, IO}
import cats.implicits._
import ch.epfl.bluebrain.nexus.storage.File.FileAttributes
import ch.epfl.bluebrain.nexus.storage.StorageError.{InternalError, OperationTimedOut}
import ch.epfl.bluebrain.nexus.storage.attributes.AttributesCacheActor.Protocol._
import ch.epfl.bluebrain.nexus.storage.config.AppConfig.DigestConfig
import com.typesafe.scalalogging.Logger

import scala.util.control.NonFatal

trait AttributesCache[F[_]] {

  
  def asyncComputePut(filePath: Path, algorithm: String): Unit
}

object AttributesCache {
  private[this] val logger = Logger[this.type]

  def apply[F[_], Source](implicit
      system: ActorSystem,
      clock: Clock,
      tm: Timeout,
      F: Effect[F],
      computation: AttributesComputation[F, Source],
      config: DigestConfig
  ): AttributesCache[F] =
    apply(system.actorOf(AttributesCacheActor.props(computation)))

  private[attributes] def apply[F[_]](
      underlying: ActorRef
  )(implicit system: ActorSystem, tm: Timeout, F: Effect[F]): AttributesCache[F] =
    new AttributesCache[F] {
      implicit private val contextShift: ContextShift[IO] = IO.contextShift(system.dispatcher)

      override def get(filePath: Path): F[FileAttributes] =
        IO.fromFuture(IO.shift(system.dispatcher) >> IO(underlying ? Get(filePath)))
          .to[F]
          .flatMap[FileAttributes] {
            case attributes: FileAttributes => F.pure(attributes)
            case other                      =>
              logger.error(s"Received unexpected reply from the file attributes cache: '$other'")
              F.raiseError(InternalError("Unexpected reply from the file attributes cache"))
          }
          .recoverWith {
            case _: AskTimeoutException =>
              F.raiseError(OperationTimedOut("reply from the file attributes cache timed out"))
            case NonFatal(th)           =>
              logger.error("Exception caught while exchanging messages with the file attributes cache", th)
              F.raiseError(InternalError("Exception caught while exchanging messages with the file attributes cache"))
          }

      override def asyncComputePut(filePath: Path, algorithm: String): Unit =
        underlying ! Compute(filePath)

    }
} 
Example 41
Source File: AttributesComputation.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.storage.attributes

import java.nio.file.{Files, Path}
import java.security.MessageDigest

import akka.http.scaladsl.model.HttpCharsets.`UTF-8`
import akka.http.scaladsl.model.MediaTypes.{`application/octet-stream`, `application/x-tar`}
import akka.http.scaladsl.model.{ContentType, MediaType, MediaTypes}
import akka.stream.Materializer
import akka.stream.scaladsl.{Keep, Sink}
import akka.util.ByteString
import cats.effect.Effect
import cats.implicits._
import ch.epfl.bluebrain.nexus.storage.File.{Digest, FileAttributes}
import ch.epfl.bluebrain.nexus.storage.StorageError.InternalError
import ch.epfl.bluebrain.nexus.storage._
import org.apache.commons.io.FilenameUtils

import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success, Try}

trait AttributesComputation[F[_], Source] {

  
  implicit def akkaAttributes[F[_]](implicit
      ec: ExecutionContext,
      mt: Materializer,
      F: Effect[F]
  ): AttributesComputation[F, AkkaSource] =
    (path: Path, algorithm: String) => {
      if (!Files.exists(path)) F.raiseError(InternalError(s"Path not found '$path'"))
      else
        Try(MessageDigest.getInstance(algorithm)) match {
          case Success(msgDigest) =>
            val isDir  = Files.isDirectory(path)
            val source = if (isDir) folderSource(path) else fileSource(path)
            source
              .alsoToMat(sinkSize)(Keep.right)
              .toMat(sinkDigest(msgDigest)) { (bytesF, digestF) =>
                (bytesF, digestF).mapN {
                  case (bytes, digest) => FileAttributes(path.toAkkaUri, bytes, digest, detectMediaType(path, isDir))
                }
              }
              .run()
              .to[F]
          case Failure(_)         => F.raiseError(InternalError(s"Invalid algorithm '$algorithm'."))
        }

    }
} 
Example 42
Source File: IamIdentitiesClient.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.storage

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.client.RequestBuilding.Get
import akka.http.scaladsl.model.HttpRequest
import akka.http.scaladsl.model.headers.OAuth2BearerToken
import akka.http.scaladsl.unmarshalling.FromEntityUnmarshaller
import akka.util.ByteString
import cats.effect.{ContextShift, Effect, IO}
import cats.implicits._
import ch.epfl.bluebrain.nexus.rdf.implicits._
import ch.epfl.bluebrain.nexus.storage.IamIdentitiesClient.Identity._
import ch.epfl.bluebrain.nexus.storage.IamIdentitiesClient._
import ch.epfl.bluebrain.nexus.storage.IamIdentitiesClientError.IdentitiesSerializationError
import ch.epfl.bluebrain.nexus.storage.config.IamClientConfig
import de.heikoseeberger.akkahttpcirce.ErrorAccumulatingCirceSupport.{DecodingFailures => AccDecodingFailures}
import io.circe.Decoder.Result
import io.circe.{Decoder, DecodingFailure, HCursor}

import scala.concurrent.ExecutionContext

class IamIdentitiesClient[F[_]](config: IamClientConfig)(implicit F: Effect[F], as: ActorSystem)
    extends JsonLdCirceSupport {

  private val um: FromEntityUnmarshaller[Caller]      = unmarshaller[Caller]
  implicit private val ec: ExecutionContext           = as.dispatcher
  implicit private val contextShift: ContextShift[IO] = IO.contextShift(ec)

  def apply()(implicit credentials: Option[AccessToken]): F[Caller] =
    credentials match {
      case Some(token) => execute(Get(config.identitiesIri.asAkka).addCredentials(OAuth2BearerToken(token.value)))
      case None        => F.pure(Caller.anonymous)
    }

  private def execute(req: HttpRequest): F[Caller] = {
    IO.fromFuture(IO(Http().singleRequest(req))).to[F].flatMap { resp =>
      if (resp.status.isSuccess())
        IO.fromFuture(IO(um(resp.entity))).to[F].recoverWith {
          case err: AccDecodingFailures => F.raiseError(IdentitiesSerializationError(err.getMessage))
          case err: Error               => F.raiseError(IdentitiesSerializationError(err.getMessage))
        }
      else
        IO.fromFuture(IO(resp.entity.dataBytes.runFold(ByteString(""))(_ ++ _).map(_.utf8String)))
          .to[F]
          .flatMap { err => F.raiseError(IamIdentitiesClientError.unsafe(resp.status, err)) }
    }
  }

}

object IamIdentitiesClient {

  
    final case class Authenticated(realm: String) extends Identity

    private def decodeAnonymous(hc: HCursor): Result[Subject] =
      hc.get[String]("@type").flatMap {
        case "Anonymous" => Right(Anonymous)
        case _           => Left(DecodingFailure("Cannot decode Anonymous Identity", hc.history))
      }

    private def decodeUser(hc: HCursor): Result[Subject] =
      (hc.get[String]("subject"), hc.get[String]("realm")).mapN {
        case (subject, realm) => User(subject, realm)
      }

    private def decodeGroup(hc: HCursor): Result[Identity] =
      (hc.get[String]("group"), hc.get[String]("realm")).mapN {
        case (group, realm) => Group(group, realm)
      }

    private def decodeAuthenticated(hc: HCursor): Result[Identity] =
      hc.get[String]("realm").map(Authenticated)

    private val attempts =
      List[HCursor => Result[Identity]](decodeAnonymous, decodeUser, decodeGroup, decodeAuthenticated)

    implicit val identityDecoder: Decoder[Identity] =
      Decoder.instance { hc =>
        attempts.foldLeft(Left(DecodingFailure("Unexpected", hc.history)): Result[Identity]) {
          case (acc @ Right(_), _) => acc
          case (_, f)              => f(hc)
        }
      }
  }

} 
Example 43
Source File: BookingRoutes.scala    From ticket-booking-aecor   with Apache License 2.0 5 votes vote down vote up
package ru.pavkin.booking.booking.endpoint

import cats.effect.Effect
import cats.implicits._
import io.circe.syntax._
import org.http4s.HttpRoutes
import org.http4s.circe._
import org.http4s.dsl.Http4sDsl
import ru.pavkin.booking.common.models.{BookingKey, ClientId}

final class BookingRoutes[F[_]: Effect](ops: BookingEndpoint[F]) extends Http4sDsl[F] {

  implicit val placeBookingDecoder = jsonOf[F, PlaceBookingRequest]
  implicit val cancelBookingDecoder = jsonOf[F, CancelBookingRequest]

  private val placeBooking: HttpRoutes[F] = HttpRoutes.of {
    case r @ POST -> Root / userId / "bookings" =>
      r.as[PlaceBookingRequest]
        .flatMap(
          r =>
            ops.placeBooking(ClientId(userId), r.concertId, r.seats).flatMap {
              case Left(err) => BadRequest(err.toString)
              case Right(_)  => Ok()
          }
        )
  }

  private val cancelBooking: HttpRoutes[F] = HttpRoutes.of {
    case r @ DELETE -> Root / userId / "bookings" / bookingId =>
      r.as[CancelBookingRequest]
        .flatMap(
          r =>
            ops.cancelBooking(ClientId(userId), BookingKey(bookingId), r.reason).flatMap {
              case Left(err) => BadRequest(err.toString)
              case Right(_)  => Ok()
          }
        )
  }

  private val clientBookings: HttpRoutes[F] = HttpRoutes.of {
    case GET -> Root / userId / "bookings" =>
      ops.clientBookings(ClientId(userId)).flatMap { bookings =>
        Ok(bookings.asJson)
      }
  }

  val routes: HttpRoutes[F] =
    placeBooking <+> clientBookings <+> cancelBooking

} 
Example 44
Source File: ProducerOf.scala    From skafka   with MIT License 5 votes vote down vote up
package com.evolutiongaming.skafka.producer

import cats.effect.{Bracket, ContextShift, Effect, Resource}
import cats.{Defer, Monad, ~>}
import com.evolutiongaming.smetrics.MeasureDuration

import scala.concurrent.ExecutionContext

trait ProducerOf[F[_]] {

  def apply(config: ProducerConfig): Resource[F, Producer[F]]
}

object ProducerOf {

  def apply[F[_] : Effect : ContextShift : MeasureDuration](
    executorBlocking: ExecutionContext,
    metrics: Option[ProducerMetrics[F]] = None
  ): ProducerOf[F] = new ProducerOf[F] {

    def apply(config: ProducerConfig) = {
      for {
        producer <- Producer.of[F](config, executorBlocking)
      } yield {
        metrics.fold(producer)(producer.withMetrics[Throwable])
      }
    }
  }


  implicit class ProducerOfOps[F[_]](val self: ProducerOf[F]) extends AnyVal {

    def mapK[G[_] : Monad : Defer](
      fg: F ~> G,
      gf: G ~> F)(implicit
      B: Bracket[F, Throwable]
    ): ProducerOf[G] = new ProducerOf[G] {

      def apply(config: ProducerConfig) = {
        for {
          a <- self(config).mapK(fg)
        } yield {
          a.mapK(fg, gf)
        }
      }
    }
  }
} 
Example 45
Source File: CatsEffect.scala    From cats-effect-testing   with Apache License 2.0 5 votes vote down vote up
package cats.effect.testing.specs2

import cats.effect.{Effect, Resource, Sync}
import cats.effect.syntax.effect._
import org.specs2.execute.{AsResult, Failure, Result}

import scala.concurrent.duration._
import scala.language.higherKinds

trait CatsEffect {

  protected val Timeout: Duration = 10.seconds

  implicit def effectAsResult[F[_]: Effect, R](implicit R: AsResult[R]): AsResult[F[R]] = new AsResult[F[R]] {
    def asResult(t: => F[R]): Result =
      t.toIO.unsafeRunTimed(Timeout)
        .map(R.asResult(_))
        .getOrElse(Failure(s"expectation timed out after $Timeout"))
  }

  implicit def resourceAsResult[F[_]: Effect, R](implicit R: AsResult[R]): AsResult[Resource[F,R]] = new AsResult[Resource[F,R]]{
    def asResult(t: => Resource[F, R]): Result = 
      t.use(r => Sync[F].delay(R.asResult(r)))
        .toIO
        .unsafeRunTimed(Timeout)
        .getOrElse(Failure(s"expectation timed out after $Timeout"))
  }
} 
Example 46
Source File: EffectCheckerAsserting.scala    From cats-effect-testing   with Apache License 2.0 5 votes vote down vote up
package cats.effect.testing.scalatest.scalacheck

import cats.effect.Effect
import org.scalactic.source
import org.scalatest.exceptions._
import org.scalatestplus.scalacheck.CheckerAsserting

class EffectCheckerAsserting[F[_], A](implicit F: Effect[F])
    extends CheckerAsserting.CheckerAssertingImpl[F[A]] {

  override type Result = F[Unit]

  override def succeed(result: F[A]): (Boolean, Option[Throwable]) =
    F.toIO(result)
      .attempt
      .unsafeRunSync()
      .fold(e => (false, Some(e)), _ => (true, None))

  override def indicateSuccess(message: => String): Result = F.unit

  override def indicateFailure(
      messageFun: StackDepthException => String,
      undecoratedMessage: => String,
      scalaCheckArgs: List[Any],
      scalaCheckLabels: List[String],
      optionalCause: Option[Throwable],
      pos: source.Position
  ): Result = {
    val error = new GeneratorDrivenPropertyCheckFailedException(
      messageFun,
      optionalCause,
      pos,
      None,
      undecoratedMessage,
      scalaCheckArgs,
      None,
      scalaCheckLabels
    )

    F.raiseError(error)
  }

} 
Example 47
Source File: TransactionRoute.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.example.transaction

import java.util.UUID

import aecor.example.account
import aecor.example.account.AccountId
import aecor.example.common.Amount
import cats.effect.{Effect, Sync}
import cats.implicits._
import org.http4s.HttpRoutes
import org.http4s.circe.CirceEntityDecoder
import org.http4s.dsl.Http4sDsl
import io.circe.generic.auto._


trait TransactionService[F[_]] {
  def authorizePayment(transactionId: TransactionId,
                       from: From[AccountId],
                       to: To[AccountId],
                       amount: Amount): F[TransactionRoute.ApiResult]
}

object TransactionRoute {

  sealed trait ApiResult
  object ApiResult {
    case object Authorized extends ApiResult
    case class Declined(reason: String) extends ApiResult
  }

  final case class CreateTransactionRequest(from: From[AccountId],
                                            to: To[AccountId],
                                            amount: Amount)

  object TransactionIdVar {
    def unapply(arg: String): Option[TransactionId] = TransactionId(arg).some
  }

  private final class Builder[F[_]: Sync](service: TransactionService[F]) extends Http4sDsl[F] with CirceEntityDecoder {
    def routes: HttpRoutes[F] = HttpRoutes.of[F] {
      case req @ PUT -> Root / "transactions" / TransactionIdVar(transactionId) =>
        for {
          body <- req.as[CreateTransactionRequest]
          CreateTransactionRequest(from, to, amount) = body
          resp <- service.authorizePayment(transactionId, from, to, amount).flatMap {
            case ApiResult.Authorized =>
              Ok("Authorized")
            case ApiResult.Declined(reason) =>
              BadRequest(s"Declined: $reason")
          }
        } yield resp
      case POST -> Root / "test" =>
        service
          .authorizePayment(
            TransactionId(UUID.randomUUID.toString),
            From(account.EventsourcedAlgebra.rootAccountId),
            To(AccountId("foo")),
            Amount(1)
          )
          .flatMap {
          case ApiResult.Authorized =>
            Ok("Authorized")
          case ApiResult.Declined(reason) =>
            BadRequest(s"Declined: $reason")
        }
    }
  }

  def apply[F[_]: Effect](api: TransactionService[F]): HttpRoutes[F] =
    new Builder(api).routes

} 
Example 48
Source File: deployment.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.example.transaction
import java.util.UUID

import aecor.example.common.Timestamp
import aecor.example.transaction.transaction.Transactions
import aecor.runtime.Eventsourced
import aecor.runtime.akkapersistence.AkkaPersistenceRuntime
import aecor.util.Clock
import cats.implicits._
import cats.effect.{ ContextShift, Effect }
import scodec.codecs.implicits._

object deployment {
  def deploy[F[_]: Effect: ContextShift](runtime: AkkaPersistenceRuntime[UUID],
                                         clock: Clock[F]): F[Transactions[F]] =
    runtime
      .deploy(
        "Transaction",
        EventsourcedAlgebra.behavior[F].enrich(clock.instant.map(Timestamp(_))),
        EventsourcedAlgebra.tagging
      )
      .map(Eventsourced.Entities.rejectable(_))
} 
Example 49
Source File: deployment.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.example.account
import java.util.UUID

import aecor.example.common.Timestamp
import aecor.runtime.Eventsourced
import aecor.runtime.akkapersistence.AkkaPersistenceRuntime
import aecor.util.Clock
import cats.effect.{ ContextShift, Effect }
import cats.implicits._

object deployment {
  def deploy[F[_]: Effect: ContextShift](runtime: AkkaPersistenceRuntime[UUID],
                                         clock: Clock[F]): F[Accounts[F]] =
    runtime
      .deploy(
        "Account",
        EventsourcedAlgebra.behavior[F].enrich(clock.instant.map(Timestamp(_))),
        EventsourcedAlgebra.tagging
      )
      .map(Eventsourced.Entities.rejectable(_))
} 
Example 50
Source File: RebalanceEvents.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.kafkadistributedprocessing.internal

import java.util

import aecor.data.Committable
import cats.effect.concurrent.Deferred
import cats.effect.implicits._
import cats.effect.{ ConcurrentEffect, Effect }
import cats.implicits._
import fs2.Stream
import fs2.concurrent.Queue
import org.apache.kafka.clients.consumer.ConsumerRebalanceListener
import org.apache.kafka.common
import org.apache.kafka.common.TopicPartition

import scala.collection.JavaConverters._

private[kafkadistributedprocessing] object RebalanceEvents {
  final class UsePartiallyApplied[F[_]] {
    def subscribe[A](
      f: ConsumerRebalanceListener => F[Unit]
    )(implicit F: ConcurrentEffect[F]): F[Stream[F, Committable[F, RebalanceEvent]]] =
      for {
        queue <- Queue.unbounded[F, Committable[F, RebalanceEvent]]
        listener = new Listener[F](
          event =>
            Deferred[F, Unit]
              .flatMap { completion =>
                queue.enqueue1(Committable(completion.complete(()), event)) >> completion.get
            }
        )
        _ <- f(listener)
      } yield queue.dequeue
  }

  def apply[F[_]]: UsePartiallyApplied[F] = new UsePartiallyApplied[F]

  sealed abstract class RebalanceEvent
  object RebalanceEvent {
    final case class PartitionsRevoked(partitions: Set[TopicPartition]) extends RebalanceEvent
    final case class PartitionsAssigned(partitions: Set[TopicPartition]) extends RebalanceEvent
  }

  private final class Listener[F[_]: Effect](processEvent: RebalanceEvent => F[Unit])
      extends ConsumerRebalanceListener {

    override def onPartitionsRevoked(partitions: util.Collection[common.TopicPartition]): Unit =
      processEvent(RebalanceEvent.PartitionsRevoked(partitions.asScala.toSet)).toIO
        .unsafeRunSync()

    override def onPartitionsAssigned(partitions: util.Collection[common.TopicPartition]): Unit =
      processEvent(RebalanceEvent.PartitionsAssigned(partitions.asScala.toSet)).toIO
        .unsafeRunSync()
  }
} 
Example 51
Source File: DefaultScheduleEventJournal.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.schedule.process

import java.util.UUID

import aecor.data.{ Committable, ConsumerId, EntityEvent, EventTag }
import aecor.runtime.akkapersistence.readside.CommittableEventJournalQuery
import aecor.schedule.{ ScheduleBucketId, ScheduleEvent }
import aecor.util.effect._
import akka.stream.Materializer
import akka.stream.scaladsl.{ Keep, Sink }
import cats.effect.Effect
import cats.implicits._

object DefaultScheduleEventJournal {
  def apply[F[_]: Effect](
    consumerId: ConsumerId,
    parallelism: Int,
    aggregateJournal: CommittableEventJournalQuery[F, UUID, ScheduleBucketId, ScheduleEvent],
    eventTag: EventTag
  )(implicit materializer: Materializer): DefaultScheduleEventJournal[F] =
    new DefaultScheduleEventJournal(consumerId, parallelism, aggregateJournal, eventTag)
}

final class DefaultScheduleEventJournal[F[_]: Effect](
  consumerId: ConsumerId,
  parallelism: Int,
  aggregateJournal: CommittableEventJournalQuery[F, UUID, ScheduleBucketId, ScheduleEvent],
  eventTag: EventTag
)(implicit materializer: Materializer)
    extends ScheduleEventJournal[F] {
  override def processNewEvents(
    f: EntityEvent[ScheduleBucketId, ScheduleEvent] => F[Unit]
  ): F[Unit] =
    Effect[F].fromFuture {
      aggregateJournal
        .currentEventsByTag(eventTag, consumerId)
        .mapAsync(parallelism)(_.map(_.event).traverse(f).unsafeToFuture())
        .fold(Committable.unit[F])(Keep.right)
        .mapAsync(1)(_.commit.unsafeToFuture())
        .runWith(Sink.ignore)
    }.void
} 
Example 52
Source File: PeriodicProcessRuntime.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.schedule.process

import aecor.distributedprocessing.{ AkkaStreamProcess, DistributedProcessing }
import aecor.util.effect._
import akka.NotUsed
import akka.actor.ActorSystem
import akka.stream.Materializer
import akka.stream.scaladsl.Source
import cats.effect.{ ContextShift, Effect }

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

object PeriodicProcessRuntime {
  def apply[F[_]: Effect: ContextShift](
    name: String,
    tickInterval: FiniteDuration,
    processCycle: F[Unit]
  )(implicit materializer: Materializer): PeriodicProcessRuntime[F] =
    new PeriodicProcessRuntime[F](name, tickInterval, processCycle)
}

class PeriodicProcessRuntime[F[_]: Effect: ContextShift](
  name: String,
  tickInterval: FiniteDuration,
  processCycle: F[Unit]
)(implicit materializer: Materializer) {

  private def source =
    Source
      .tick(0.seconds, tickInterval, processCycle)
      .mapAsync(1)(_.unsafeToFuture())
      .mapMaterializedValue(_ => NotUsed)

  def run(system: ActorSystem): F[DistributedProcessing.KillSwitch[F]] =
    DistributedProcessing(system)
      .start[F](s"$name-Process", List(AkkaStreamProcess[F](source)))

} 
Example 53
Source File: DefaultSchedule.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.schedule

import java.time.LocalDateTime
import java.util.UUID

import aecor.data._
import aecor.runtime.akkapersistence.readside.{ CommittableEventJournalQuery, JournalEntry }
import aecor.util.Clock
import akka.NotUsed
import akka.stream.scaladsl.Source
import cats.effect.Effect
import cats.implicits._
import aecor.util.effect._

import scala.concurrent.duration.FiniteDuration

private[schedule] class DefaultSchedule[F[_]: Effect](
  clock: Clock[F],
  buckets: ScheduleBucketId => ScheduleBucket[F],
  bucketLength: FiniteDuration,
  aggregateJournal: CommittableEventJournalQuery[F, UUID, ScheduleBucketId, ScheduleEvent],
  eventTag: EventTag
) extends Schedule[F] {
  override def addScheduleEntry(scheduleName: String,
                                entryId: String,
                                correlationId: String,
                                dueDate: LocalDateTime): F[Unit] =
    for {
      zone <- clock.zone
      scheduleBucket = dueDate.atZone(zone).toEpochSecond / bucketLength.toSeconds
      _ <- buckets(ScheduleBucketId(scheduleName, scheduleBucket.toString))
            .addScheduleEntry(entryId, correlationId, dueDate)
    } yield ()

  override def committableScheduleEvents(
    scheduleName: String,
    consumerId: ConsumerId
  ): Source[Committable[F, JournalEntry[UUID, ScheduleBucketId, ScheduleEvent]], NotUsed] =
    aggregateJournal
      .eventsByTag(eventTag, ConsumerId(scheduleName + consumerId.value))
      .flatMapConcat {
        case m if m.value.event.entityKey.scheduleName == scheduleName => Source.single(m)
        case other =>
          Source
            .fromFuture(other.commit.unsafeToFuture())
            .flatMapConcat(
              _ => Source.empty[Committable[F, JournalEntry[UUID, ScheduleBucketId, ScheduleEvent]]]
            )
      }

} 
Example 54
Source File: DistributedProcessingWorker.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.distributedprocessing

import aecor.distributedprocessing.DistributedProcessing._
import aecor.distributedprocessing.DistributedProcessingWorker.KeepRunning
import aecor.distributedprocessing.serialization.Message
import cats.effect.syntax.effect._
import akka.actor.{ Actor, ActorLogging, Props, Status }
import akka.pattern._
import cats.effect.Effect
import cats.implicits._

private[aecor] object DistributedProcessingWorker {
  def props[F[_]: Effect](processWithId: Int => Process[F], processName: String): Props =
    Props(new DistributedProcessingWorker[F](processWithId, processName))

  final case class KeepRunning(workerId: Int) extends Message
}

private[aecor] final class DistributedProcessingWorker[F[_]: Effect](
  processFor: Int => Process[F],
  processName: String
) extends Actor
    with ActorLogging {
  import context.dispatcher

  case class ProcessStarted(process: RunningProcess[F])
  case object ProcessTerminated

  var killSwitch: Option[F[Unit]] = None

  override def postStop: Unit =
    killSwitch.foreach(_.toIO.unsafeRunSync())

  def receive: Receive = {
    case KeepRunning(workerId) =>
      log.info("[{}] Starting process {}", workerId, processName)
      processFor(workerId).run
        .map(ProcessStarted)
        .toIO
        .unsafeToFuture() pipeTo self
      context.become {
        case ProcessStarted(RunningProcess(watchTermination, terminate)) =>
          log.info("[{}] Process started {}", workerId, processName)
          killSwitch = Some(terminate)
          watchTermination.toIO.map(_ => ProcessTerminated).unsafeToFuture() pipeTo self
          context.become {
            case Status.Failure(e) =>
              log.error(e, "Process failed {}", processName)
              throw e
            case ProcessTerminated =>
              log.error("Process terminated {}", processName)
              throw new IllegalStateException(s"Process terminated $processName")
          }
        case Status.Failure(e) =>
          log.error(e, "Process failed to start {}", processName)
          throw e
        case KeepRunning(_) => ()
      }
  }
} 
Example 55
Source File: DistributedProcessing.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.distributedprocessing

import java.net.URLEncoder
import java.nio.charset.StandardCharsets

import aecor.distributedprocessing.DistributedProcessing.{ KillSwitch, Process }
import aecor.distributedprocessing.DistributedProcessingWorker.KeepRunning
import aecor.util.effect._
import akka.actor.ActorSystem
import akka.cluster.sharding.{ ClusterSharding, ClusterShardingSettings }
import akka.pattern.{ BackoffOpts, BackoffSupervisor, ask }
import akka.util.Timeout
import cats.effect.Effect
import cats.implicits._

import scala.concurrent.duration.{ FiniteDuration, _ }
final class DistributedProcessing private (system: ActorSystem) {

  
  def start[F[_]: Effect](name: String,
                          processes: List[Process[F]],
                          settings: DistributedProcessingSettings =
                            DistributedProcessingSettings.default(system)): F[KillSwitch[F]] =
    Effect[F].delay {
      val opts = BackoffOpts
        .onFailure(
          DistributedProcessingWorker.props(processes, name),
          "worker",
          settings.minBackoff,
          settings.maxBackoff,
          settings.randomFactor
        )

      val props = BackoffSupervisor.props(opts)

      val region = ClusterSharding(system).start(
        typeName = name,
        entityProps = props,
        settings = settings.clusterShardingSettings,
        extractEntityId = {
          case c @ KeepRunning(workerId) => (workerId.toString, c)
        },
        extractShardId = {
          case KeepRunning(workerId) => (workerId % settings.numberOfShards).toString
          case other                 => throw new IllegalArgumentException(s"Unexpected message [$other]")
        }
      )

      val regionSupervisor = system.actorOf(
        DistributedProcessingSupervisor
          .props(processes.size, region, settings.heartbeatInterval),
        "DistributedProcessingSupervisor-" + URLEncoder
          .encode(name, StandardCharsets.UTF_8.name())
      )
      implicit val timeout = Timeout(settings.shutdownTimeout)
      KillSwitch {
        Effect[F].fromFuture {
          regionSupervisor ? DistributedProcessingSupervisor.GracefulShutdown
        }.void
      }
    }
}

object DistributedProcessing {
  def apply(system: ActorSystem): DistributedProcessing = new DistributedProcessing(system)
  final case class KillSwitch[F[_]](shutdown: F[Unit]) extends AnyVal
  final case class RunningProcess[F[_]](watchTermination: F[Unit], shutdown: F[Unit])
  final case class Process[F[_]](run: F[RunningProcess[F]]) extends AnyVal
}

final case class DistributedProcessingSettings(minBackoff: FiniteDuration,
                                               maxBackoff: FiniteDuration,
                                               randomFactor: Double,
                                               shutdownTimeout: FiniteDuration,
                                               numberOfShards: Int,
                                               heartbeatInterval: FiniteDuration,
                                               clusterShardingSettings: ClusterShardingSettings)

object DistributedProcessingSettings {
  def default(clusterShardingSettings: ClusterShardingSettings): DistributedProcessingSettings =
    DistributedProcessingSettings(
      minBackoff = 3.seconds,
      maxBackoff = 10.seconds,
      randomFactor = 0.2,
      shutdownTimeout = 10.seconds,
      numberOfShards = 100,
      heartbeatInterval = 2.seconds,
      clusterShardingSettings = clusterShardingSettings
    )

  def default(system: ActorSystem): DistributedProcessingSettings =
    default(ClusterShardingSettings(system))
} 
Example 56
Source File: GenericAkkaRuntime.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.runtime.akkageneric

import aecor.encoding.WireProtocol.Encoded
import aecor.encoding.syntax._
import aecor.encoding.{ KeyDecoder, KeyEncoder, WireProtocol }
import aecor.runtime.akkageneric.GenericAkkaRuntime.KeyedCommand
import aecor.runtime.akkageneric.GenericAkkaRuntimeActor.CommandResult
import aecor.runtime.akkageneric.serialization.Message
import aecor.util.effect._
import akka.actor.ActorSystem
import akka.cluster.sharding.{ ClusterSharding, ShardRegion }
import akka.pattern._
import akka.util.Timeout
import cats.effect.Effect
import cats.implicits._
import cats.tagless.FunctorK
import cats.tagless.syntax.functorK._
import cats.~>
import scodec.bits.BitVector

object GenericAkkaRuntime {
  def apply(system: ActorSystem): GenericAkkaRuntime =
    new GenericAkkaRuntime(system)
  private[akkageneric] final case class KeyedCommand(key: String, bytes: BitVector) extends Message
}

final class GenericAkkaRuntime private (system: ActorSystem) {
  def runBehavior[K: KeyEncoder: KeyDecoder, M[_[_]]: FunctorK, F[_]](
    typeName: String,
    createBehavior: K => F[M[F]],
    settings: GenericAkkaRuntimeSettings = GenericAkkaRuntimeSettings.default(system)
  )(implicit M: WireProtocol[M], F: Effect[F]): F[K => M[F]] =
    F.delay {
      val props = GenericAkkaRuntimeActor.props[K, M, F](createBehavior, settings.idleTimeout)

      val extractEntityId: ShardRegion.ExtractEntityId = {
        case KeyedCommand(entityId, c) =>
          (entityId, GenericAkkaRuntimeActor.Command(c))
      }

      val numberOfShards = settings.numberOfShards

      val extractShardId: ShardRegion.ExtractShardId = {
        case KeyedCommand(key, _) =>
          String.valueOf(scala.math.abs(key.hashCode) % numberOfShards)
        case other => throw new IllegalArgumentException(s"Unexpected message [$other]")
      }

      val shardRegion = ClusterSharding(system).start(
        typeName = typeName,
        entityProps = props,
        settings = settings.clusterShardingSettings,
        extractEntityId = extractEntityId,
        extractShardId = extractShardId
      )

      val keyEncoder = KeyEncoder[K]

      key =>
        M.encoder.mapK(new (Encoded ~> F) {

          implicit val askTimeout: Timeout = Timeout(settings.askTimeout)

          override def apply[A](fa: Encoded[A]): F[A] = F.suspend {
            val (bytes, decoder) = fa
            F.fromFuture {
                shardRegion ? KeyedCommand(keyEncoder(key), bytes)
              }
              .flatMap {
                case result: CommandResult =>
                  decoder.decodeValue(result.bytes).lift[F]
                case other =>
                  F.raiseError(
                    new IllegalArgumentException(s"Unexpected response [$other] from shard region")
                  )
              }
          }
        })
    }
} 
Example 57
Source File: effect.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.util

import cats.effect.{ Async, Effect, IO }

import scala.concurrent.{ Future, Promise }

object effect {
  implicit final class AecorEffectOps[F[_], A](val self: F[A]) extends AnyVal {
    @inline final def unsafeToFuture()(implicit F: Effect[F]): Future[A] = {
      val p = Promise[A]
      F.runAsync(self) {
          case Right(a) => IO { p.success(a); () }
          case Left(e)  => IO { p.failure(e); () }
        }
        .unsafeRunSync()
      p.future
    }
  }

  implicit final class AecorLiftIOOps[F[_]](val self: Async[F]) extends AnyVal {
    def fromFuture[A](future: => Future[A]): F[A] =
      IO.fromFuture(IO(future))(IO.contextShift(scala.concurrent.ExecutionContext.global)).to(self)
  }
} 
Example 58
Source File: JournalQuery.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.runtime.akkapersistence.readside

import aecor.Has
import aecor.data.{ EntityEvent, EventTag, TagConsumer }
import aecor.runtime.KeyValueStore
import akka.NotUsed
import akka.stream.scaladsl.Source
import cats.effect.Effect

final case class JournalEntry[O, K, A](offset: O, event: EntityEvent[K, A]) {
  def map[B](f: A => B): JournalEntry[O, K, B] = copy(event = event.map(f))
}

object JournalEntry {
  implicit def aecorHasInstanceForEvent[X, O, K, A](
    implicit A: Has[EntityEvent[K, A], X]
  ): Has[JournalEntry[O, K, A], X] =
    A.contramap(_.event)

  implicit def aecorHasInstanceForOffset[X, O, K, A](
    implicit A: Has[O, X]
  ): Has[JournalEntry[O, K, A], X] = A.contramap(_.offset)

}

trait JournalQuery[O, K, E] {
  def eventsByTag(tag: EventTag,
                  offset: Option[O]): Source[JournalEntry[O, K, E], NotUsed]

  def currentEventsByTag(tag: EventTag,
                         offset: Option[O]): Source[JournalEntry[O, K, E], NotUsed]

  def committable[F[_]: Effect](
    offsetStore: KeyValueStore[F, TagConsumer, O]
  ): CommittableEventJournalQuery[F, O, K, E] =
    new CommittableEventJournalQuery(this, offsetStore)
} 
Example 59
Source File: CommittableEventJournalQuery.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.runtime.akkapersistence.readside

import aecor.data.{ Committable, ConsumerId, EventTag, TagConsumer }
import aecor.runtime.KeyValueStore
import aecor.util.effect._
import akka.NotUsed
import akka.stream.scaladsl.Source
import cats.effect.Effect

final class CommittableEventJournalQuery[F[_]: Effect, O, K, E] private[akkapersistence] (
  underlying: JournalQuery[O, K, E],
  offsetStore: KeyValueStore[F, TagConsumer, O]
) {

  private def mkCommittableSource(tag: EventTag,
                                  consumerId: ConsumerId,
                                  inner: Option[O] => Source[JournalEntry[O, K, E], NotUsed]) = {
    val tagConsumerId = TagConsumer(tag, consumerId)
    Source
      .single(NotUsed)
      .mapAsync(1) { _ =>
        offsetStore.getValue(tagConsumerId).unsafeToFuture()
      }
      .flatMapConcat(inner)
      .map(x => Committable(offsetStore.setValue(tagConsumerId, x.offset), x))
  }

  def eventsByTag(tag: EventTag,
                  consumerId: ConsumerId): Source[Committable[F, JournalEntry[O, K, E]], NotUsed] =
    mkCommittableSource(tag, consumerId, underlying.eventsByTag(tag, _))

  def currentEventsByTag(
    tag: EventTag,
    consumerId: ConsumerId
  ): Source[Committable[F, JournalEntry[O, K, E]], NotUsed] =
    mkCommittableSource(tag, consumerId, underlying.currentEventsByTag(tag, _))
}

private[akkapersistence] object CommittableEventJournalQuery {
  def apply[F[_]: Effect, Offset, K, E](
    underlying: JournalQuery[Offset, K, E],
    offsetStore: KeyValueStore[F, TagConsumer, Offset]
  ): CommittableEventJournalQuery[F, Offset, K, E] =
    new CommittableEventJournalQuery(underlying, offsetStore)
} 
Example 60
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 61
Source File: AkkaPersistenceRuntime.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.runtime.akkapersistence

import aecor.data.{ EventsourcedBehavior, Tagging }
import aecor.encoding.WireProtocol.Encoded
import aecor.encoding.syntax._
import aecor.encoding.{ KeyDecoder, KeyEncoder, WireProtocol }
import aecor.runtime.akkapersistence.AkkaPersistenceRuntime._
import aecor.runtime.akkapersistence.AkkaPersistenceRuntimeActor.CommandResult
import aecor.runtime.akkapersistence.readside.{ AkkaPersistenceEventJournalQuery, JournalQuery }
import aecor.runtime.akkapersistence.serialization.{ Message, PersistentDecoder, PersistentEncoder }
import aecor.util.effect._
import akka.actor.ActorSystem
import akka.cluster.sharding.{ ClusterSharding, ShardRegion }
import akka.pattern.ask
import akka.util.Timeout
import cats.effect.Effect
import cats.implicits._
import cats.tagless.FunctorK
import cats.tagless.syntax.functorK._
import cats.~>
import scodec.bits.BitVector

object AkkaPersistenceRuntime {
  def apply[O](system: ActorSystem, journalAdapter: JournalAdapter[O]): AkkaPersistenceRuntime[O] =
    new AkkaPersistenceRuntime(system, journalAdapter)

  private[akkapersistence] final case class EntityCommand(entityKey: String,
                                                          commandBytes: BitVector)
      extends Message
}

class AkkaPersistenceRuntime[O] private[akkapersistence] (system: ActorSystem,
                                                          journalAdapter: JournalAdapter[O]) {
  def deploy[M[_[_]]: FunctorK, F[_], State, Event: PersistentEncoder: PersistentDecoder, K: KeyEncoder: KeyDecoder](
    typeName: String,
    behavior: EventsourcedBehavior[M, F, State, Event],
    tagging: Tagging[K],
    snapshotPolicy: SnapshotPolicy[State] = SnapshotPolicy.never,
    settings: AkkaPersistenceRuntimeSettings = AkkaPersistenceRuntimeSettings.default(system)
  )(implicit M: WireProtocol[M], F: Effect[F]): F[K => M[F]] =
    F.delay {
      val props =
        AkkaPersistenceRuntimeActor.props(
          typeName,
          behavior,
          snapshotPolicy,
          tagging,
          settings.idleTimeout,
          journalAdapter.writeJournalId,
          snapshotPolicy.pluginId
        )

      val extractEntityId: ShardRegion.ExtractEntityId = {
        case EntityCommand(entityId, bytes) =>
          (entityId, AkkaPersistenceRuntimeActor.HandleCommand(bytes))
      }

      val numberOfShards = settings.numberOfShards

      val extractShardId: ShardRegion.ExtractShardId = {
        case EntityCommand(entityId, _) =>
          (scala.math.abs(entityId.hashCode) % numberOfShards).toString
        case other => throw new IllegalArgumentException(s"Unexpected message [$other]")
      }

      val shardRegion = ClusterSharding(system).start(
        typeName = typeName,
        entityProps = props,
        settings = settings.clusterShardingSettings,
        extractEntityId = extractEntityId,
        extractShardId = extractShardId
      )

      val keyEncoder = KeyEncoder[K]

      key =>
        M.encoder.mapK(new (Encoded ~> F) {

          implicit val askTimeout: Timeout = Timeout(settings.askTimeout)

          override def apply[A](fa: Encoded[A]): F[A] = F.suspend {
            val (bytes, decoder) = fa
            F.fromFuture {
                shardRegion ? EntityCommand(keyEncoder(key), bytes)
              }
              .flatMap {
                case CommandResult(resultBytes) =>
                  decoder.decodeValue(resultBytes).lift[F]
                case other =>
                  F.raiseError(
                    new IllegalArgumentException(s"Unexpected response [$other] from shard region")
                  )
              }
          }
        })
    }

  def journal[K: KeyDecoder, E: PersistentDecoder]: JournalQuery[O, K, E] =
    AkkaPersistenceEventJournalQuery[O, K, E](journalAdapter)
} 
Example 62
Source File: DefaultJournalCassandraSession.scala    From aecor   with MIT License 5 votes vote down vote up
package akka.persistence.cassandra

import akka.Done
import akka.actor.{ ActorSystem, ExtendedActorSystem }
import akka.event.Logging
import akka.persistence.cassandra.Session.Init
import akka.persistence.cassandra.session.CassandraSessionSettings
import akka.persistence.cassandra.session.scaladsl.CassandraSession
import cats.effect.{ ContextShift, Effect }
import cats.implicits._

object DefaultJournalCassandraSession {

  
  def apply[F[_]: ContextShift](
    system: ActorSystem,
    metricsCategory: String,
    init: Init[F],
    sessionProvider: Option[SessionProvider] = None
  )(implicit F: Effect[F]): F[CassandraSession] = F.delay {
    val log = Logging(system, classOf[CassandraSession])
    val provider = sessionProvider.getOrElse(
      SessionProvider(
        system.asInstanceOf[ExtendedActorSystem],
        system.settings.config.getConfig("cassandra-journal")
      )
    )
    val settings = CassandraSessionSettings(system.settings.config.getConfig("cassandra-journal"))
    new CassandraSession(system, provider, settings, system.dispatcher, log, metricsCategory, { x =>
      F.toIO(init(Session[F](x)).as(Done)).unsafeToFuture()
    })
  }
} 
Example 63
Source File: EffectStep.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.steps.cats

import cats.data.{ EitherT, NonEmptyList }
import cats.effect.Effect
import cats.syntax.either._
import com.github.agourlay.cornichon.core.ScenarioRunner.{ errorsToFailureStep, successLog }
import com.github.agourlay.cornichon.core._
import monix.eval.Task
import monix.execution.Scheduler

import scala.concurrent.duration.Duration

case class EffectStep[F[_]: Effect](title: String, effect: ScenarioContext => F[Either[CornichonError, Session]], show: Boolean = true) extends SessionValueStep {

  def setTitle(newTitle: String): Step = copy(title = newTitle)

  override def runSessionValueStep(runState: RunState): Task[Either[NonEmptyList[CornichonError], Session]] =
    Task.fromEffect(effect(runState.scenarioContext)).map(_.leftMap(NonEmptyList.one))

  override def onError(errors: NonEmptyList[CornichonError], runState: RunState, executionTime: Duration): (LogInstruction, FailedStep) =
    errorsToFailureStep(this, runState.depth, errors, Some(executionTime))

  override def logOnSuccess(result: Session, runState: RunState, executionTime: Duration): LogInstruction =
    successLog(title, runState.depth, show, executionTime)

}

object EffectStep {

  def fromEitherT[F[_]: Effect](title: String, effect: ScenarioContext => EitherT[F, CornichonError, Session], show: Boolean = true): Step = {
    val effectT: ScenarioContext => F[Either[CornichonError, Session]] = s => effect(s).value
    EffectStep(title, effectT, show)
  }

  def fromSync(title: String, effect: ScenarioContext => Session, show: Boolean = true): Step = {
    import Scheduler.Implicits.global
    val effectF: ScenarioContext => Task[Either[CornichonError, Session]] = s => Task.now(effect(s).asRight)
    EffectStep(title, effectF, show)
  }

  def fromSyncE(title: String, effect: ScenarioContext => Either[CornichonError, Session], show: Boolean = true): Step = {
    import Scheduler.Implicits.global
    val effectF: ScenarioContext => Task[Either[CornichonError, Session]] = s => Task.now(effect(s))
    EffectStep(title, effectF, show)
  }

  def fromAsync[F[_]: Effect](title: String, effect: ScenarioContext => F[Session], show: Boolean = true): Step = {
    import Scheduler.Implicits.global
    val effectF: ScenarioContext => Task[Either[CornichonError, Session]] = s => Task.fromEffect(effect(s)).map(Right.apply)
    EffectStep(title, effectF, show)
  }

} 
Example 64
Source File: implicits.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.doobie.instances

import cats.effect.{Effect, IO, SyncEffect}
import doobie.ConnectionIO
import tofu.lift.Lift

object implicits extends DoobieImplicits1

private[instances] trait DoobieImplicits1 extends DoobieImplicits2 {
  @inline final implicit def liftToConnectionIOViaIOImplicit[F[_]: Lift[*[_], IO]]: LiftToConnectionIOViaIO[F] =
    liftToConnectionIOViaIO
}

private[instances] trait DoobieImplicits2 extends DoobieImplicitsScalaVersionSpecific {
  @inline final implicit def liftEffectToConnectionIOImplicit[F[_]: Effect]: LiftEffectToConnectionIO[F] =
    liftEffectToConnectionIO

  @inline final implicit def liftSyncEffectToConnectionIOImplicit[F[_]: SyncEffect]: LiftSyncEffectToConnectionIO[F] =
    liftSyncEffectToConnectionIO

  @inline final implicit def liftToConnectionRIOImplicit[F[_], R](implicit
      L: Lift[F, ConnectionIO]
  ): LiftToConnectionRIO[F, R] = liftToConnectionRIO
} 
Example 65
Source File: DoobieInstances.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.doobie.instances

import cats.data.ReaderT
import cats.effect.{Effect, IO, SyncEffect}
import cats.effect.syntax.effect._
import cats.effect.syntax.syncEffect._
import doobie.ConnectionIO
import doobie.free.connection.AsyncConnectionIO
import tofu.HasProvide
import tofu.doobie.ConnectionRIO
import tofu.lift.Lift

private[instances] trait DoobieInstances {
  final def liftToConnectionIOViaIO[F[_]: Lift[*[_], IO]]: LiftToConnectionIOViaIO[F] = new LiftToConnectionIOViaIO

  final def liftEffectToConnectionIO[F[_]: Effect]: LiftEffectToConnectionIO[F] = new LiftEffectToConnectionIO

  final def liftSyncEffectToConnectionIO[F[_]: SyncEffect]: LiftSyncEffectToConnectionIO[F] =
    new LiftSyncEffectToConnectionIO

  final def liftToConnectionRIO[F[_], R](implicit L: Lift[F, ConnectionIO]): LiftToConnectionRIO[F, R] =
    new LiftToConnectionRIO

  final def liftProvideToConnectionRIO[F[_], G[_], R](implicit
      HP: HasProvide[G, F, R],
      L: Lift[F, ConnectionIO]
  ): LiftProvideToConnectionRIO[F, G, R] = new LiftProvideToConnectionRIO
}

final class LiftToConnectionIOViaIO[F[_]](implicit L: Lift[F, IO]) extends Lift[F, ConnectionIO] {
  def lift[A](fa: F[A]): ConnectionIO[A] = AsyncConnectionIO.liftIO(L.lift(fa))
}

final class LiftEffectToConnectionIO[F[_]: Effect] extends Lift[F, ConnectionIO] {
  def lift[A](fa: F[A]): ConnectionIO[A] = AsyncConnectionIO.liftIO(fa.toIO)
}

final class LiftSyncEffectToConnectionIO[F[_]: SyncEffect] extends Lift[F, ConnectionIO] {
  def lift[A](fa: F[A]): ConnectionIO[A] = fa.runSync[ConnectionIO]
}

final class LiftToConnectionRIO[F[_], R](implicit L: Lift[F, ConnectionIO]) extends Lift[F, ConnectionRIO[R, *]] {
  def lift[A](fa: F[A]): ConnectionRIO[R, A] = ReaderT.liftF(L.lift(fa))
}

final class LiftProvideToConnectionRIO[F[_], G[_], R](implicit HP: HasProvide[G, F, R], L: Lift[F, ConnectionIO])
    extends Lift[G, ConnectionRIO[R, *]] {
  def lift[A](fa: G[A]): ConnectionRIO[R, A] = ReaderT(ctx => L.lift(HP.runContext(fa)(ctx)))
} 
Example 66
Source File: unlift.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.syntax

import cats.effect.{CancelToken, ConcurrentEffect, Effect, ExitCase, Fiber, IO, SyncIO}
import cats.{FlatMap, Functor, ~>}
import tofu.lift.Unlift

object unlift {

  implicit final class UnliftEffectOps[F[_], G[_]](private val U: Unlift[F, G]) extends AnyVal {
    def effect(implicit G: Functor[G], E: Effect[F]): G[Effect[G]] =
      G.map(U.unlift) { unliftF =>
        new EffectInstance[F, G] {
          def toG: F ~> G           = U.liftF
          def toF: G ~> F           = unliftF
          implicit def F: Effect[F] = E
        }
      }

    def effectWith[A](f: Effect[G] => G[A])(implicit G: FlatMap[G], E: Effect[F]): G[A] =
      G.flatMap(U.unlift) { unliftF =>
        val eff = new EffectInstance[F, G] {
          def toG: F ~> G           = U.liftF
          def toF: G ~> F           = unliftF
          implicit def F: Effect[F] = E
        }
        f(eff)
      }

    def concurrentEffect(implicit G: Functor[G], CE: ConcurrentEffect[F]): G[ConcurrentEffect[G]] =
      G.map(U.unlift) { unliftF =>
        new ConcurrentEffectInstance[F, G] {
          def toG: F ~> G                     = U.liftF
          def toF: G ~> F                     = unliftF
          implicit def F: ConcurrentEffect[F] = CE
        }
      }

    def concurrentEffectWith[A](f: ConcurrentEffect[G] => G[A])(implicit G: FlatMap[G], CE: ConcurrentEffect[F]): G[A] =
      G.flatMap(U.unlift) { unliftF =>
        val ce = new ConcurrentEffectInstance[F, G] {
          def toG: F ~> G                     = U.liftF
          def toF: G ~> F                     = unliftF
          implicit def F: ConcurrentEffect[F] = CE
        }
        f(ce)
      }

  }

  private[unlift] trait EffectInstance[F[_], G[_]] extends Effect[G] {
    def toG: F ~> G
    def toF: G ~> F
    implicit def F: Effect[F]

    def pure[A](x: A): G[A] = toG(F.pure(x))

    def flatMap[A, B](ga: G[A])(f: A => G[B]): G[B] = toG(F.flatMap(toF(ga))(a => toF(f(a))))

    def tailRecM[A, B](a: A)(f: A => G[Either[A, B]]): G[B] = toG(F.tailRecM(a)(a => toF(f(a))))

    def raiseError[A](e: Throwable): G[A] = toG(F.raiseError(e))

    def handleErrorWith[A](ga: G[A])(f: Throwable => G[A]): G[A] = toG(F.handleErrorWith(toF(ga))(t => toF(f(t))))

    def bracketCase[A, B](acquire: G[A])(use: A => G[B])(release: (A, ExitCase[Throwable]) => G[Unit]): G[B] =
      toG(F.bracketCase(toF(acquire))(a => toF(use(a)))((a, e) => toF(release(a, e))))

    def suspend[A](thunk: => G[A]): G[A] = toG(F.suspend(toF(thunk)))

    def async[A](k: (Either[Throwable, A] => Unit) => Unit): G[A] = toG(F.async(k))

    def asyncF[A](k: (Either[Throwable, A] => Unit) => G[Unit]): G[A] = toG(F.asyncF[A](cb => toF(k(cb))))

    def runAsync[A](ga: G[A])(cb: Either[Throwable, A] => IO[Unit]): SyncIO[Unit] = F.runAsync(toF(ga))(cb)
  }

  private[unlift] trait ConcurrentEffectInstance[F[_], G[_]] extends EffectInstance[F, G] with ConcurrentEffect[G] {
    implicit def F: ConcurrentEffect[F]

    def start[A](ga: G[A]): G[Fiber[G, A]] = toG(F.map(F.start(toF(ga)))(_.mapK(toG)))

    def racePair[A, B](ga: G[A], gb: G[B]): G[Either[(A, Fiber[G, B]), (Fiber[G, A], B)]] =
      toG(F.map(F.racePair(toF(ga), toF(gb))) {
        case Left((a, fb))  => Left((a, fb.mapK(toG)))
        case Right((fa, b)) => Right((fa.mapK(toG), b))
      })

    def runCancelable[A](ga: G[A])(cb: Either[Throwable, A] => IO[Unit]): SyncIO[CancelToken[G]] =
      F.runCancelable(toF(ga))(cb).map(toG(_))
  }

} 
Example 67
Source File: UnliftSuite.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu

import cats.Monad
import cats.effect.{ConcurrentEffect, Effect}
import tofu.lift.Unlift
import tofu.syntax.unlift._

object UnliftSuite {

  private def needsEffect[G[_]: Effect]: G[Unit] = Effect[G].unit

  private def needsConcurrentEffect[G[_]: ConcurrentEffect]: G[Unit] = ConcurrentEffect[G].unit

  def checkUnliftEffectSyntax[F[_]: Effect, G[_]: Monad](implicit U: Unlift[F, G]): Unit = {
    Unlift[F, G].effect: G[Effect[G]]
    Unlift[F, G].effectWith(implicit E => needsEffect[G])
    ()
  }

  def checkUnliftCESyntax[F[_]: ConcurrentEffect, G[_]: Monad](implicit U: Unlift[F, G]): Unit = {
    Unlift[F, G].concurrentEffect: G[ConcurrentEffect[G]]
    Unlift[F, G].concurrentEffectWith(implicit CE => needsConcurrentEffect[G])
    ()
  }

} 
Example 68
Source File: TestSqsConsumerBuilder.scala    From fs2-aws   with MIT License 5 votes vote down vote up
package fs2.aws.testkit
import cats.effect.Effect
import com.amazon.sqs.javamessaging.SQSConnection
import fs2.aws.testkit.TestSqsConsumerBuilder.TestSQSConsumer
import fs2.aws.sqs.{ ConsumerBuilder, SQSConsumer }
import javax.jms.MessageListener
import org.mockito.scalatest.MockitoSugar

class TestSqsConsumerBuilder[F[_]: Effect] extends ConsumerBuilder[F] {
  override def start: F[SQSConsumer] =
    Effect[F].delay(new TestSQSConsumer)
}

object TestSqsConsumerBuilder extends MockitoSugar {
  class TestSQSConsumer extends SQSConsumer {
    override def callback: MessageListener = mock[MessageListener]
    override def startConsumer(): Unit     = ()
    override def shutdown(): Unit          = ()
    override def connection: SQSConnection = mock[SQSConnection]
  }
} 
Example 69
Source File: SQSConsumerBuilder.scala    From fs2-aws   with MIT License 5 votes vote down vote up
package fs2.aws.sqs

import cats.effect.Effect
import com.amazon.sqs.javamessaging.{ ProviderConfiguration, SQSConnection, SQSConnectionFactory }
import com.amazonaws.services.sqs.AmazonSQSClientBuilder
import eu.timepit.refined.auto._
import javax.jms.{ MessageListener, Session }

import scala.language.higherKinds

class SQSConsumerBuilder[F[_]](val sqsConfig: SqsConfig, val listener: MessageListener)(
  implicit F: Effect[F]
) extends ConsumerBuilder[F] {

  val start: F[SQSConsumer] = {
    F.delay {
      new SQSConsumer {
        override val callback: MessageListener = listener

        val connectionFactory = new SQSConnectionFactory(
          new ProviderConfiguration(),
          AmazonSQSClientBuilder.defaultClient()
        )
        override val connection: SQSConnection = connectionFactory.createConnection

        override def startConsumer(): Unit = {
          val session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE)
          val cons    = session.createConsumer(session.createQueue(sqsConfig.queueName))
          cons.setMessageListener(callback)
          connection.start()
        }

        override def shutdown(): Unit =
          connection.stop()
      }
    }
  }
}

object SQSConsumerBuilder {
  def apply[F[_]](sqsConfig: SqsConfig, listener: MessageListener)(
    implicit F: Effect[F]
  ): SQSConsumerBuilder[F] = new SQSConsumerBuilder[F](sqsConfig, listener)
} 
Example 70
Source File: S3Client.scala    From fs2-aws   with MIT License 5 votes vote down vote up
package fs2.aws.internal

import java.io.InputStream

import cats.effect.Effect
import com.amazonaws.services.s3.AmazonS3
import com.amazonaws.services.s3.model._

import scala.collection.JavaConverters._
import scala.util.control.Exception

private[aws] object S3Client {
  def apply[F[_]](s3: AmazonS3) = new S3ClientImpl[F](s3)
}

private[aws] class S3ClientImpl[F[_]](c: AmazonS3) extends S3Client[F] {
  override def client: AmazonS3 = c
}

private[aws] trait S3Client[F[_]] {

  def client: AmazonS3

  def getObjectContentOrError(
    getObjectRequest: GetObjectRequest
  )(implicit F: Effect[F]): F[Either[Throwable, InputStream]] =
    F.delay(Exception.nonFatalCatch either client.getObject(getObjectRequest).getObjectContent)

  def getObjectContent(getObjectRequest: GetObjectRequest)(implicit F: Effect[F]): F[InputStream] =
    F.delay(client.getObject(getObjectRequest).getObjectContent)

  def initiateMultipartUpload(
    initiateMultipartUploadRequest: InitiateMultipartUploadRequest
  )(implicit F: Effect[F]): F[InitiateMultipartUploadResult] =
    F.delay(client.initiateMultipartUpload(initiateMultipartUploadRequest))

  def uploadPart(uploadPartRequest: UploadPartRequest)(implicit F: Effect[F]): F[UploadPartResult] =
    F.delay(client.uploadPart(uploadPartRequest))

  def completeMultipartUpload(
    completeMultipartUploadRequest: CompleteMultipartUploadRequest
  )(implicit F: Effect[F]): F[CompleteMultipartUploadResult] =
    F.delay(client.completeMultipartUpload(completeMultipartUploadRequest))

  def s3ObjectSummaries(
    listObjectsV2Request: ListObjectsV2Request
  )(implicit F: Effect[F]): F[List[S3ObjectSummary]] =
    F.delay(client.listObjectsV2(listObjectsV2Request).getObjectSummaries.asScala.toList)

  def getObject(objectRequest: GetObjectRequest)(implicit F: Effect[F]): F[S3Object] =
    F.delay(client.getObject(objectRequest))
} 
Example 71
Source File: package.scala    From fs2-aws   with MIT License 5 votes vote down vote up
package fs2.aws

import java.io._

import cats.effect.{ Effect, IO }
import com.amazonaws.SdkClientException
import com.amazonaws.services.s3.AmazonS3
import com.amazonaws.services.s3.model.{ AmazonS3Exception, GetObjectRequest, S3ObjectInputStream }
import fs2.aws.internal._
import org.apache.http.client.methods.HttpRequestBase
import scala.io.Source

package object utils {
  val s3TestClient: S3Client[IO] = new S3Client[IO] {

    override def client: AmazonS3 =
      throw new NotImplementedError("s3 client shouldn't be used in this test client")

    override def getObjectContentOrError(
      getObjectRequest: GetObjectRequest
    )(implicit e: Effect[IO]): IO[Either[Throwable, InputStream]] =
      getObjectRequest match {
        case goe: GetObjectRequest => {
          IO[Either[Throwable, ByteArrayInputStream]] {
            val fileContent: Array[Byte] =
              try {
                Source.fromResource(goe.getKey).mkString.getBytes
              } catch {
                case _: FileNotFoundException => throw new AmazonS3Exception("File not found")
                case e: Throwable             => throw e
              }
            goe.getRange match {
              case Array(x, y) =>
                if (y > fileContent.length)
                  Right(new ByteArrayInputStream(fileContent.slice(x.toInt, fileContent.length)))
                else Right(new ByteArrayInputStream(fileContent.slice(x.toInt, y.toInt)))
            }

          } map {
            case Left(e) => Left(e)
            case Right(is) =>
              Thread.sleep(500) // simulate a call to S3
              Right(new S3ObjectInputStream(is, new HttpRequestBase {
                def getMethod = ""
              }))
          }
        }
        case _ => throw new SdkClientException("Invalid GetObjectRequest")
      }

    override def getObjectContent(
      getObjectRequest: GetObjectRequest
    )(implicit e: Effect[IO]): IO[InputStream] =
      IO[ByteArrayInputStream] {
        val fileContent: Array[Byte] =
          try {
            val testS3Resource = Option(getObjectRequest.getVersionId) match {
              case Some(version) => s"${getObjectRequest.getKey}_v$version"
              case None          => getObjectRequest.getKey
            }
            Source.fromResource(testS3Resource).mkString.getBytes
          } catch {
            case _: FileNotFoundException => throw new AmazonS3Exception("File not found")
            case e: Throwable             => throw e
          }
        new ByteArrayInputStream(fileContent)

      }.map { is =>
        Thread.sleep(500) // simulate a call to S3
        new S3ObjectInputStream(is, new HttpRequestBase {
          def getMethod = ""
        })
      }
  }

} 
Example 72
Source File: CatsHelpers.scala    From nelson   with Apache License 2.0 5 votes vote down vote up
package nelson

import cats.Eval
import cats.effect.{Effect, IO, Timer}
import cats.free.Cofree
import cats.syntax.functor._
import cats.syntax.monadError._

import fs2.{Pipe, Sink, Stream}

import quiver.{Context, Decomp, Graph}

import java.util.concurrent.TimeoutException

import scala.concurrent.ExecutionContext
import scala.concurrent.duration.FiniteDuration
import scala.collection.immutable.{Stream => SStream}

object CatsHelpers {
  implicit class NelsonEnrichedIO[A](val io: IO[A]) extends AnyVal {
    
  private type Tree[A] = Cofree[SStream, A]

  private def flattenTree[A](tree: Tree[A]): SStream[A] = {
    def go(tree: Tree[A], xs: SStream[A]): SStream[A] =
      SStream.cons(tree.head, tree.tail.value.foldRight(xs)(go(_, _)))
    go(tree, SStream.Empty)
  }

  private def Node[A](root: A, forest: => SStream[Tree[A]]): Tree[A] =
    Cofree[SStream, A](root, Eval.later(forest))

  implicit class NelsonEnrichedGraph[N, A, B](val graph: Graph[N, A, B]) extends AnyVal {
    def reachable(v: N): Vector[N] =
      xdfWith(Seq(v), _.successors, _.vertex)._1.flatMap(flattenTree)

    def xdfWith[C](vs: Seq[N], d: Context[N, A, B] => Seq[N], f: Context[N, A, B] => C): (Vector[Tree[C]], Graph[N, A, B]) =
      if (vs.isEmpty || graph.isEmpty) (Vector(), graph)
      else graph.decomp(vs.head) match {
        case Decomp(None, g) => g.xdfWith(vs.tail, d, f)
        case Decomp(Some(c), g) =>
          val (xs, _) = g.xdfWith(d(c), d, f)
          val (ys, g3) = g.xdfWith(vs.tail, d, f)
          (Node(f(c), xs.toStream) +: ys, g3)
      }
  }
} 
Example 73
Source File: package.scala    From scala-pet-store   with Apache License 2.0 5 votes vote down vote up
package io.github.pauljamescleary.petstore
package infrastructure.repository

import cats.implicits._
import cats.effect.{Async, ContextShift, Effect, IO}
import config._
import _root_.doobie.Transactor
import io.circe.config.parser

import scala.concurrent.ExecutionContext

package object doobie {
  def getTransactor[F[_]: Async: ContextShift](cfg: DatabaseConfig): Transactor[F] =
    Transactor.fromDriverManager[F](
      cfg.driver, // driver classname
      cfg.url, // connect URL (driver-specific)
      cfg.user, // user
      cfg.password, // password
    )

  
  def initializedTransactor[F[_]: Effect: Async: ContextShift]: F[Transactor[F]] =
    for {
      petConfig <- parser.decodePathF[F, PetStoreConfig]("petstore")
      _ <- DatabaseConfig.initializeDb(petConfig.db)
    } yield getTransactor(petConfig.db)

  lazy val testEc = ExecutionContext.Implicits.global

  implicit lazy val testCs = IO.contextShift(testEc)

  lazy val testTransactor = initializedTransactor[IO].unsafeRunSync()
} 
Example 74
Source File: Fs2AsyncQueue.scala    From sttp   with Apache License 2.0 5 votes vote down vote up
package sttp.client.impl.fs2

import cats.effect.{Effect, IO}
import fs2.concurrent.InspectableQueue
import sttp.client.ws.internal.AsyncQueue
import sttp.model.ws.WebSocketBufferFull

import scala.language.higherKinds

class Fs2AsyncQueue[F[_], A](queue: InspectableQueue[F, A])(implicit F: Effect[F]) extends AsyncQueue[F, A] {
  override def offer(t: A): Unit = {
    F.toIO(queue.offer1(t))
      .flatMap {
        case true  => IO.unit
        case false => IO.raiseError(new WebSocketBufferFull())
      }
      .unsafeRunSync()
  }

  override def poll: F[A] = queue.dequeue1
} 
Example 75
Source File: RecoverLog.scala    From polynote   with Apache License 2.0 5 votes vote down vote up
package polynote

import java.nio.channels.FileChannel
import java.nio.file.{Files, Paths, StandardOpenOption}
import java.time.Instant

import cats.effect.Effect
import polynote.app.{Args, MainArgs}
import polynote.kernel.logging.Logging
import polynote.messages.{Message, Notebook, NotebookUpdate, ShortList}
import polynote.server.AppEnv
import zio.{Ref, Runtime, Task, UIO, ZIO}
import zio.ZIO.effectTotal
import zio.blocking.effectBlocking
import fs2.Stream
import polynote.server.repository.{FileBasedRepository, NotebookContent}
import polynote.server.repository.format.ipynb.IPythonFormat
import polynote.server.repository.fs.WAL
import polynote.server.taskConcurrent
import scodec.bits.ByteVector
import scodec.stream.decode
import scodec.codecs
import scodec.stream.decode.StreamDecoder

object RecoverLog {

  def replay(messages: Stream[Task, (Instant, Message)], ref: Ref[Notebook], log: Logging.Service): UIO[Unit] = messages.map(_._2).evalMap {
    case nb: Notebook => ref.set(nb)
    case upd: NotebookUpdate => ref.update {
      nb => try {
        upd.applyTo(nb)
      } catch {
        case err: Throwable =>
          log.errorSync(Some("Dropped update because an error occurred when applying it"), err)
          nb
      }
    }
    case _ => ZIO.unit
  }.compile.drain.catchAll {
    err =>
      log.error(Some("Error occurred while replaying the log; printing the final state anyway."), err)
  }

  def main(implicit ev: Effect[Task]): ZIO[AppEnv, String, Int] = for {
    args     <- ZIO.access[MainArgs](_.get[Args].rest)
    path     <- ZIO(args.head).flatMap(pathStr => effectBlocking(Paths.get(pathStr).toRealPath())).orDie
    is       <- effectBlocking(FileChannel.open(path, StandardOpenOption.READ)).orDie
    log      <- Logging.access
    _        <- Logging.info(s"Reading log entries from ${path}...")
    messages  = WAL.decoder.decodeMmap(is)
    ref      <- Ref.make(Notebook("", ShortList.Nil, None))
    _        <- replay(messages, ref, log)
    format    = new IPythonFormat
    result   <- ref.get
    encoded  <- format.encodeNotebook(NotebookContent(result.cells, result.config)).orDie
    _        <- effectTotal(println(encoded))
  } yield 0
} 
Example 76
Source File: Main.scala    From polynote   with Apache License 2.0 5 votes vote down vote up
package polynote

import java.io.File

import cats.effect.Effect
import polynote.app.Environment
import polynote.config.{KernelIsolation, PolynoteConfig}
import polynote.kernel.{BaseEnv, GlobalEnv, Kernel, LocalKernel, LocalSparkKernel}
import polynote.kernel.environment.{Config, CurrentNotebook}
import polynote.kernel.environment.Env.LayerOps
import polynote.kernel.interpreter.Interpreter
import polynote.kernel.remote.{RemoteKernel, RemoteSparkKernel}
import polynote.messages.NotebookConfig
import polynote.server.{AppEnv, Server}
import polynote.app.{Args, MainArgs, globalEnv}
import polynote.server.auth.IdentityProvider
import polynote.server.repository.NotebookRepository
import polynote.server.repository.fs.FileSystems
import zio.{Has, Task, ULayer, ZIO, ZLayer}

abstract class Main
object Main extends polynote.app.App {
  private implicit val taskEffect: Effect[Task] = zio.interop.catz.taskEffectInstance

  val main: ZIO[AppEnv, Nothing, Int] =
    MainArgs.access.flatMap {
      args => args.command match {
        case "server"  => new Server().main
        case "run"     => NotebookRunner.main
        case "recover" => RecoverLog.main
        case other     => ZIO.dieMessage(s"Unknown command $other (expected server)")
      }
    }.catchAll {
      str => ZIO.effectTotal {
        System.err.println(str)
        System.err.println()
      }.as(1)
    }

  override def main(args: List[String]): ZIO[Environment, Nothing, Int] = main.provideSomeLayer[BaseEnv] {
    Args.parse(args).orDie andThen
      ((Config.layer.orDie ++ kernelFactory ++ FileSystems.live) andThen
        globalEnv.orDie andThen NotebookRepository.live)
  }

  private val kernelFactory: ULayer[Kernel.Factory] = ZLayer.succeed {
    Kernel.Factory.choose {
      for {
        notebook <- CurrentNotebook.get
        config   <- Config.access
      } yield {
        val notebookConfig = notebook.config.getOrElse(NotebookConfig.empty)
        val isSpark = notebookConfig.sparkTemplate.nonEmpty || {
          notebookConfig.sparkConfig match {
            case None                     => false
            case Some(map) if map.isEmpty => false
            case Some(_)                  => true
          }
        }

        config.behavior.kernelIsolation match {
          case KernelIsolation.Always | KernelIsolation.SparkOnly if isSpark => RemoteSparkKernel
          case KernelIsolation.Never                              if isSpark => LocalSparkKernel
          case KernelIsolation.Always                                        => RemoteKernel
          case _                                                             => LocalKernel
        }
      }
    }
  }

} 
Example 77
Source File: AirlinesJob.scala    From core   with Apache License 2.0 5 votes vote down vote up
package com.smartbackpackerapp.airlines

import cats.effect.{Effect, IO}
import cats.syntax.flatMap._
import cats.syntax.functor._
import com.smartbackpackerapp.airlines.parser.{AirlineFile, AllowanceFile}
import fs2.StreamApp.ExitCode
import fs2.{Stream, StreamApp}

object AirlinesApp extends AirlinesJob[IO]

// See: https://wikitravel.org/en/Discount_airlines_in_Europe
class AirlinesJob[F[_]](implicit F: Effect[F]) extends StreamApp[F] {

  private val ctx = new AirlinesModule[F]

  case object MissingArgument extends Exception("There should be 2 arguments in the following order: `Airline file path` and `Allowance file path`.")

  private def putStrLn(value: String): Stream[F, Unit] = Stream.eval(F.delay(println(value)))

  def readArgs(args: List[String]): F[(AirlineFile, AllowanceFile)] = {
    val ifEmpty = F.raiseError[String](MissingArgument)
    for {
      x <- args.headOption.fold(ifEmpty)(F.delay(_))
      y <- args.lastOption.fold(ifEmpty)(F.delay(_))
    } yield (AirlineFile(x), AllowanceFile(y))
  }

  def program(airlineFile: AirlineFile,
              allowanceFile: AllowanceFile): Stream[F, Unit] =
    for {
      _ <- if (ctx.devDbUrl.nonEmpty) putStrLn(s"DEV DB connection established: ${ctx.devDbUrl}")
           else putStrLn(s"DB connection established: ${ctx.dbUrl}")
      _ <- putStrLn("Starting job")
      _ <- ctx.airlinesInsertData(airlineFile, allowanceFile).run
      _ <- putStrLn("Job finished successfully")
    } yield ()

  def stream(args: List[String], requestShutdown: F[Unit]): Stream[F, ExitCode] =
    for {
      files    <- Stream.eval(readArgs(args))
      (x, y)   = files
      exitCode <- program(x, y).drain
    } yield exitCode

} 
Example 78
Source File: Server.scala    From core   with Apache License 2.0 5 votes vote down vote up
package com.smartbackpackerapp

import cats.Parallel
import cats.effect.Effect
import com.smartbackpackerapp.http.auth.JwtTokenAuthMiddleware
import fs2.StreamApp.ExitCode
import fs2.{Scheduler, Stream, StreamApp}
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.http4s.client.blaze.Http1Client
import org.http4s.server.blaze.BlazeBuilder

object Server extends HttpServer[Task, Task.Par]

class HttpServer[F[_], G[_]](implicit F: Effect[F], P: Parallel[F, G]) extends StreamApp[F] {

  // Workaround until something like mirror comes out: https://github.com/typelevel/cats/pull/2019
  implicit val parallel: Parallel[F, F] = P.asInstanceOf[Parallel[F, F]]

  private lazy val ApiToken: F[Option[String]] = F.delay(sys.env.get("SB_API_TOKEN"))

  override def stream(args: List[String], requestShutdown: F[Unit]): Stream[F, ExitCode] =
    Scheduler(corePoolSize = 2).flatMap { implicit scheduler =>
      for {
        httpClient      <- Http1Client.stream[F]()
        ctx             = new Module[F](httpClient)
        _               <- Stream.eval(ctx.migrateDb)
        _               <- Stream.eval(ctx.startMetricsReporter)
        apiToken        <- Stream.eval(ApiToken)
        authMiddleware  <- Stream.eval(JwtTokenAuthMiddleware[F](apiToken))
        exitCode        <- BlazeBuilder[F]
                            .bindHttp(sys.env.getOrElse("PORT", "8080").toInt, "0.0.0.0")
                            .mountService(authMiddleware(ctx.httpEndpointsWithMetrics))
                            .serve
      } yield exitCode
    }

} 
Example 79
Source File: ExchangeRateService.scala    From core   with Apache License 2.0 5 votes vote down vote up
package com.smartbackpackerapp.service

import java.net.ConnectException

import cats.MonadError
import cats.effect.Effect
import cats.syntax.all._
import com.smartbackpackerapp.common.Log
import com.smartbackpackerapp.config.SBConfiguration
import com.smartbackpackerapp.model.Currency
import io.circe.generic.auto._
import org.http4s.circe._
import org.http4s.client.{Client, UnexpectedStatus}

class ExchangeRateService[F[_] : Effect](client: Client[F],
                                         sbConfig: SBConfiguration[F])
                                        (implicit L: Log[F]) extends AbstractExchangeRateService[F](sbConfig) {

  override protected def retrieveExchangeRate(uri: String): F[CurrencyExchangeDTO] = {
    client.expect[CurrencyExchangeDTO](uri)(jsonOf[F, CurrencyExchangeDTO])
  }

}

abstract class AbstractExchangeRateService[F[_]](sbConfig: SBConfiguration[F])
                                                (implicit F: MonadError[F, Throwable], L: Log[F]) {

  protected val fixerUri: Currency => Currency => F[String] = baseCurrency => foreignCurrency => {
    val uri = sbConfig.fixerBaseUri.map(_.getOrElse("http://localhost:8081"))
    uri.map(x => s"$x/latest?base=${baseCurrency.value}&symbols=${foreignCurrency.value}")
  }

  protected def retrieveExchangeRate(uri: String): F[CurrencyExchangeDTO]

  // We don't want the whole destination service to fail if the exchange rate service is unavailable
  // so the `UnexpectedStatus` and `ConnectException` errors are treated as an empty exchange rate
  def exchangeRateFor(baseCurrency: Currency, foreignCurrency: Currency): F[CurrencyExchangeDTO] = {
    val ifEmpty = CurrencyExchangeDTO.empty(baseCurrency).pure[F]

    def performRequest(uri: String): F[CurrencyExchangeDTO] =
      retrieveExchangeRate(uri).recoverWith {
        case e: ConnectException => L.error(e).flatMap(_ => ifEmpty)
        case _: UnexpectedStatus => ifEmpty
      }

    validateCurrencies(baseCurrency, foreignCurrency).fold(ifEmpty) { _ =>
      for {
        uri <- fixerUri(baseCurrency)(foreignCurrency)
        _   <- L.info(s"Retrieving currency exchange from: $uri")
        er  <- performRequest(uri)
      } yield {
        if (er.rates.nonEmpty) er
        else er.copy(rates = Map(baseCurrency.value -> -1.0))
      }
    }
  }

  private def validateCurrencies(baseCurrency: Currency, foreignCurrency: Currency): Option[Currency] = {
    if (baseCurrency == foreignCurrency) none[Currency]
    else foreignCurrency.some
  }

} 
Example 80
Source File: ArchiveCache.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.archives

import akka.actor.{ActorSystem, NotInfluenceReceiveTimeout}
import cats.Monad
import cats.data.OptionT
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.kg.archives.ArchiveCache._
import ch.epfl.bluebrain.nexus.kg.config.AppConfig._
import ch.epfl.bluebrain.nexus.kg.resources.ResId
import ch.epfl.bluebrain.nexus.sourcing.StateMachine
import ch.epfl.bluebrain.nexus.sourcing.akka.StopStrategy
import ch.epfl.bluebrain.nexus.sourcing.akka.statemachine.AkkaStateMachine
import retry.RetryPolicy

class ArchiveCache[F[_]: Monad](ref: StateMachine[F, String, State, Command, Unit]) {

  
  def put(value: Archive): OptionT[F, Archive] =
    OptionT(ref.evaluate(value.resId.show, Write(value)).map(_.toOption.flatten))

}

object ArchiveCache {

  private[archives] type State   = Option[Archive]
  private[archives] type Command = Write
  private[archives] final case class Write(bundle: Archive) extends NotInfluenceReceiveTimeout

  final def apply[F[_]: Timer](implicit as: ActorSystem, cfg: ArchivesConfig, F: Effect[F]): F[ArchiveCache[F]] = {
    implicit val retryPolicy: RetryPolicy[F] = cfg.cache.retry.retryPolicy[F]
    val invalidationStrategy                 = StopStrategy.lapsedSinceLastInteraction[State, Command](cfg.cacheInvalidateAfter)

    val evaluate: (State, Command) => F[Either[Unit, State]] = {
      case (None, Write(bundle)) => F.pure(Right(Some(bundle)))
      case (Some(_), _)          => F.pure(Left(())) // It already exists, so we don't want to replace it
    }

    AkkaStateMachine
      .sharded[F]("archives", None, evaluate, invalidationStrategy, cfg.cache.akkaStateMachineConfig, cfg.cache.shards)
      .map(new ArchiveCache[F](_))
  }
} 
Example 81
Source File: ElasticSearchIndexer.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.indexing

import akka.actor.{ActorRef, ActorSystem, Props}
import akka.stream.scaladsl.Source
import akka.util.Timeout
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.client.types.Project
import ch.epfl.bluebrain.nexus.commons.es.client.ElasticSearchClient
import ch.epfl.bluebrain.nexus.commons.es.client.ElasticSearchClient.BulkOp
import ch.epfl.bluebrain.nexus.kg.config.AppConfig
import ch.epfl.bluebrain.nexus.kg.config.AppConfig._
import ch.epfl.bluebrain.nexus.kg.indexing.View.ElasticSearchView
import ch.epfl.bluebrain.nexus.kg.resources._
import ch.epfl.bluebrain.nexus.kg.routes.Clients
import ch.epfl.bluebrain.nexus.sourcing.projections.ProgressFlow.ProgressFlowElem
import ch.epfl.bluebrain.nexus.sourcing.projections.ProjectionProgress.NoProgress
import ch.epfl.bluebrain.nexus.sourcing.projections._
import com.typesafe.scalalogging.Logger

import scala.concurrent.ExecutionContext

// $COVERAGE-OFF$
@SuppressWarnings(Array("MaxParameters"))
object ElasticSearchIndexer {

  private implicit val log: Logger = Logger[ElasticSearchIndexer.type]

  
  final def start[F[_]: Timer](
      view: ElasticSearchView,
      resources: Resources[F],
      project: Project,
      restartOffset: Boolean
  )(
      implicit as: ActorSystem,
      actorInitializer: (Props, String) => ActorRef,
      projections: Projections[F, String],
      F: Effect[F],
      clients: Clients[F],
      config: AppConfig
  ): StreamSupervisor[F, ProjectionProgress] = {

    implicit val ec: ExecutionContext          = as.dispatcher
    implicit val p: Project                    = project
    implicit val indexing: IndexingConfig      = config.elasticSearch.indexing
    implicit val metadataOpts: MetadataOptions = MetadataOptions(linksAsIri = true, expandedLinks = true)
    implicit val tm: Timeout                   = Timeout(config.elasticSearch.askTimeout)

    val client: ElasticSearchClient[F] = clients.elasticSearch.withRetryPolicy(config.elasticSearch.indexing.retry)

    def deleteOrIndex(res: ResourceV): Option[BulkOp] =
      if (res.deprecated && !view.filter.includeDeprecated) Some(delete(res))
      else view.toDocument(res).map(doc => BulkOp.Index(view.index, res.id.value.asString, doc))

    def delete(res: ResourceV): BulkOp =
      BulkOp.Delete(view.index, res.id.value.asString)

    val initFetchProgressF: F[ProjectionProgress] =
      if (restartOffset)
        projections.recordProgress(view.progressId, NoProgress) >> view.createIndex >> F.pure(NoProgress)
      else view.createIndex >> projections.progress(view.progressId)

    val sourceF: F[Source[ProjectionProgress, _]] = initFetchProgressF.map { initial =>
      val flow = ProgressFlowElem[F, Any]
        .collectCast[Event]
        .groupedWithin(indexing.batch, indexing.batchTimeout)
        .distinct()
        .mapAsync(view.toResource(resources, _))
        .collectSome[ResourceV]
        .collect {
          case res if view.allowedSchemas(res) && view.allowedTypes(res) => deleteOrIndex(res)
          case res if view.allowedSchemas(res)                           => Some(delete(res))
        }
        .collectSome[BulkOp]
        .runAsyncBatch(client.bulk(_))()
        .mergeEmit()
        .toPersistedProgress(view.progressId, initial)

      cassandraSource(s"project=${view.ref.id}", view.progressId, initial.minProgress.offset)
        .via(flow)
        .via(kamonViewMetricsFlow(view, project))
    }
    StreamSupervisor.start(sourceF, view.progressId, actorInitializer)
  }
}
// $COVERAGE-ON$ 
Example 82
Source File: StorageIndexer.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.indexing

import java.time.Instant

import akka.actor.ActorSystem
import akka.stream.scaladsl.{Flow, Source}
import akka.util.Timeout
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.client.AdminClient
import ch.epfl.bluebrain.nexus.kg.cache.{ProjectCache, StorageCache}
import ch.epfl.bluebrain.nexus.kg.config.AppConfig
import ch.epfl.bluebrain.nexus.kg.config.AppConfig._
import ch.epfl.bluebrain.nexus.kg.config.Vocabulary.nxv
import ch.epfl.bluebrain.nexus.kg.resources._
import ch.epfl.bluebrain.nexus.kg.storage.Storage
import ch.epfl.bluebrain.nexus.sourcing.projections.ProgressFlow.{PairMsg, ProgressFlowElem}
import ch.epfl.bluebrain.nexus.sourcing.projections._
import com.typesafe.scalalogging.Logger

import scala.concurrent.ExecutionContext

// $COVERAGE-OFF$
object StorageIndexer {

  private implicit val log = Logger[StorageIndexer.type]

  def start[F[_]: Timer](storages: Storages[F], storageCache: StorageCache[F])(
      implicit projectCache: ProjectCache[F],
      F: Effect[F],
      as: ActorSystem,
      projectInitializer: ProjectInitializer[F],
      adminClient: AdminClient[F],
      config: AppConfig
  ): StreamSupervisor[F, Unit] = {

    implicit val authToken                = config.iam.serviceAccountToken
    implicit val indexing: IndexingConfig = config.keyValueStore.indexing
    implicit val ec: ExecutionContext     = as.dispatcher
    implicit val tm: Timeout              = Timeout(config.keyValueStore.askTimeout)
    val name                              = "storage-indexer"

    def toStorage(event: Event): F[Option[(Storage, Instant)]] =
      fetchProject(event.organization, event.id.parent, event.subject).flatMap { implicit project =>
        storages.fetchStorage(event.id).value.map {
          case Left(err) =>
            log.error(s"Error on event '${event.id.show} (rev = ${event.rev})', cause: '${err.msg}'")
            None
          case Right(timedStorage) => Some(timedStorage)
        }
      }

    val source: Source[PairMsg[Any], _] = cassandraSource(s"type=${nxv.Storage.value.show}", name)
    val flow: Flow[PairMsg[Any], Unit, _] = ProgressFlowElem[F, Any]
      .collectCast[Event]
      .groupedWithin(indexing.batch, indexing.batchTimeout)
      .distinct()
      .mergeEmit()
      .mapAsync(toStorage)
      .collectSome[(Storage, Instant)]
      .runAsync { case (storage, instant) => storageCache.put(storage)(instant) }()
      .flow
      .map(_ => ())

    StreamSupervisor.startSingleton(F.delay(source.via(flow)), name)
  }
}
// $COVERAGE-ON$ 
Example 83
Source File: SparqlIndexer.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.indexing

import akka.actor.{ActorRef, ActorSystem, Props}
import akka.stream.scaladsl.Source
import akka.util.Timeout
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.client.types.Project
import ch.epfl.bluebrain.nexus.commons.sparql.client.{BlazegraphClient, SparqlWriteQuery}
import ch.epfl.bluebrain.nexus.kg.config.AppConfig
import ch.epfl.bluebrain.nexus.kg.config.AppConfig._
import ch.epfl.bluebrain.nexus.kg.indexing.View.SparqlView
import ch.epfl.bluebrain.nexus.kg.resources._
import ch.epfl.bluebrain.nexus.kg.routes.Clients
import ch.epfl.bluebrain.nexus.sourcing.projections.ProgressFlow.ProgressFlowElem
import ch.epfl.bluebrain.nexus.sourcing.projections.ProjectionProgress.NoProgress
import ch.epfl.bluebrain.nexus.sourcing.projections._

import scala.concurrent.ExecutionContext

// $COVERAGE-OFF$
@SuppressWarnings(Array("MaxParameters"))
object SparqlIndexer {

  
  final def start[F[_]: Timer](
      view: SparqlView,
      resources: Resources[F],
      project: Project,
      restartOffset: Boolean
  )(
      implicit as: ActorSystem,
      actorInitializer: (Props, String) => ActorRef,
      projections: Projections[F, String],
      F: Effect[F],
      clients: Clients[F],
      config: AppConfig
  ): StreamSupervisor[F, ProjectionProgress] = {

    implicit val ec: ExecutionContext          = as.dispatcher
    implicit val p: Project                    = project
    implicit val indexing: IndexingConfig      = config.sparql.indexing
    implicit val metadataOpts: MetadataOptions = MetadataOptions(linksAsIri = true, expandedLinks = true)
    implicit val tm: Timeout                   = Timeout(config.sparql.askTimeout)

    val client: BlazegraphClient[F] =
      clients.sparql.copy(namespace = view.index).withRetryPolicy(config.sparql.indexing.retry)

    def buildInsertOrDeleteQuery(res: ResourceV): SparqlWriteQuery =
      if (res.deprecated && !view.filter.includeDeprecated) view.buildDeleteQuery(res)
      else view.buildInsertQuery(res)

    val initFetchProgressF: F[ProjectionProgress] =
      if (restartOffset)
        projections.recordProgress(view.progressId, NoProgress) >> view.createIndex >> F.pure(NoProgress)
      else view.createIndex >> projections.progress(view.progressId)

    val sourceF: F[Source[ProjectionProgress, _]] = initFetchProgressF.map { initial =>
      val flow = ProgressFlowElem[F, Any]
        .collectCast[Event]
        .groupedWithin(indexing.batch, indexing.batchTimeout)
        .distinct()
        .mapAsync(view.toResource(resources, _))
        .collectSome[ResourceV]
        .collect {
          case res if view.allowedSchemas(res) && view.allowedTypes(res) => buildInsertOrDeleteQuery(res)
          case res if view.allowedSchemas(res)                           => view.buildDeleteQuery(res)
        }
        .runAsyncBatch(client.bulk(_))()
        .mergeEmit()
        .toPersistedProgress(view.progressId, initial)
      cassandraSource(s"project=${view.ref.id}", view.progressId, initial.minProgress.offset)
        .via(flow)
        .via(kamonViewMetricsFlow(view, project))
    }
    StreamSupervisor.start(sourceF, view.progressId, actorInitializer)
  }
}
// $COVERAGE-ON$ 
Example 84
Source File: ResolverIndexer.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.indexing

import akka.actor.ActorSystem
import akka.stream.scaladsl.{Flow, Source}
import akka.util.Timeout
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.client.AdminClient
import ch.epfl.bluebrain.nexus.kg.cache.{ProjectCache, ResolverCache}
import ch.epfl.bluebrain.nexus.kg.config.AppConfig
import ch.epfl.bluebrain.nexus.kg.config.AppConfig._
import ch.epfl.bluebrain.nexus.kg.config.Vocabulary.nxv
import ch.epfl.bluebrain.nexus.kg.resolve.Resolver
import ch.epfl.bluebrain.nexus.kg.resources._
import ch.epfl.bluebrain.nexus.sourcing.projections.ProgressFlow.{PairMsg, ProgressFlowElem}
import ch.epfl.bluebrain.nexus.sourcing.projections._
import com.typesafe.scalalogging.Logger

import scala.concurrent.ExecutionContext

// $COVERAGE-OFF$
object ResolverIndexer {

  private implicit val log = Logger[ResolverIndexer.type]

  
  final def start[F[_]: Timer](resolvers: Resolvers[F], resolverCache: ResolverCache[F])(
      implicit
      projectCache: ProjectCache[F],
      as: ActorSystem,
      F: Effect[F],
      projectInitializer: ProjectInitializer[F],
      adminClient: AdminClient[F],
      config: AppConfig
  ): StreamSupervisor[F, Unit] = {
    implicit val authToken                = config.iam.serviceAccountToken
    implicit val indexing: IndexingConfig = config.keyValueStore.indexing
    implicit val ec: ExecutionContext     = as.dispatcher
    implicit val tm: Timeout              = Timeout(config.keyValueStore.askTimeout)

    val name = "resolver-indexer"

    def toResolver(event: Event): F[Option[Resolver]] =
      fetchProject(event.organization, event.id.parent, event.subject).flatMap { implicit project =>
        resolvers.fetchResolver(event.id).value.map {
          case Left(err) =>
            log.error(s"Error on event '${event.id.show} (rev = ${event.rev})', cause: '${err.msg}'")
            None
          case Right(resolver) => Some(resolver)
        }
      }

    val source: Source[PairMsg[Any], _] = cassandraSource(s"type=${nxv.Resolver.value.show}", name)
    val flow: Flow[PairMsg[Any], Unit, _] = ProgressFlowElem[F, Any]
      .collectCast[Event]
      .groupedWithin(indexing.batch, indexing.batchTimeout)
      .distinct()
      .mergeEmit()
      .mapAsync(toResolver)
      .collectSome[Resolver]
      .runAsync(resolverCache.put)()
      .flow
      .map(_ => ())

    StreamSupervisor.startSingleton(F.delay(source.via(flow)), name)
  }
}
// $COVERAGE-ON$ 
Example 85
Source File: ViewIndexer.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.indexing

import akka.actor.ActorSystem
import akka.stream.scaladsl.{Flow, Source}
import akka.util.Timeout
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.client.AdminClient
import ch.epfl.bluebrain.nexus.kg.cache.{ProjectCache, ViewCache}
import ch.epfl.bluebrain.nexus.kg.config.AppConfig
import ch.epfl.bluebrain.nexus.kg.config.AppConfig._
import ch.epfl.bluebrain.nexus.kg.config.Vocabulary.nxv
import ch.epfl.bluebrain.nexus.kg.resources._
import ch.epfl.bluebrain.nexus.sourcing.projections.ProgressFlow.{PairMsg, ProgressFlowElem}
import ch.epfl.bluebrain.nexus.sourcing.projections._
import com.typesafe.scalalogging.Logger

import scala.concurrent.ExecutionContext

// $COVERAGE-OFF$
object ViewIndexer {

  private implicit val log = Logger[ViewIndexer.type]

  def start[F[_]: Timer](views: Views[F], viewCache: ViewCache[F])(
      implicit projectCache: ProjectCache[F],
      F: Effect[F],
      as: ActorSystem,
      projectInitializer: ProjectInitializer[F],
      adminClient: AdminClient[F],
      config: AppConfig
  ): StreamSupervisor[F, Unit] = {

    implicit val authToken                = config.iam.serviceAccountToken
    implicit val indexing: IndexingConfig = config.keyValueStore.indexing
    implicit val ec: ExecutionContext     = as.dispatcher
    implicit val tm: Timeout              = Timeout(config.keyValueStore.askTimeout)
    val name                              = "view-indexer"

    def toView(event: Event): F[Option[View]] =
      fetchProject(event.organization, event.id.parent, event.subject).flatMap { implicit project =>
        views.fetchView(event.id).value.map {
          case Left(err) =>
            log.error(s"Error on event '${event.id.show} (rev = ${event.rev})', cause: '${err.msg}'")
            None
          case Right(view) => Some(view)
        }
      }

    val source: Source[PairMsg[Any], _] = cassandraSource(s"type=${nxv.View.value.show}", name)
    val flow: Flow[PairMsg[Any], Unit, _] = ProgressFlowElem[F, Any]
      .collectCast[Event]
      .groupedWithin(indexing.batch, indexing.batchTimeout)
      .distinct()
      .mergeEmit()
      .mapAsync(toView)
      .collectSome[View]
      .runAsync(viewCache.put)()
      .flow
      .map(_ => ())

    StreamSupervisor.startSingleton(F.delay(source.via(flow)), name)
  }
}
// $COVERAGE-ON$ 
Example 86
Source File: ResolverCache.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.cache

import java.util.UUID
import java.util.concurrent.ConcurrentHashMap

import akka.actor.ActorSystem
import cats.Monad
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.commons.cache.{KeyValueStore, KeyValueStoreConfig}
import ch.epfl.bluebrain.nexus.kg.cache.Cache._
import ch.epfl.bluebrain.nexus.kg.resolve.Resolver
import ch.epfl.bluebrain.nexus.kg.resources.ProjectIdentifier.ProjectRef
import ch.epfl.bluebrain.nexus.rdf.Iri.AbsoluteIri

class ResolverCache[F[_]: Effect: Timer] private (projectToCache: ConcurrentHashMap[UUID, ResolverProjectCache[F]])(
    implicit as: ActorSystem,
    config: KeyValueStoreConfig
) {

  
private class ResolverProjectCache[F[_]: Monad] private (store: KeyValueStore[F, AbsoluteIri, Resolver])
    extends Cache[F, AbsoluteIri, Resolver](store) {

  private implicit val ordering: Ordering[Resolver] = Ordering.by(_.priority)

  def get: F[List[Resolver]] = store.values.map(_.toList.sorted)

  def put(resolver: Resolver): F[Unit] =
    if (resolver.deprecated) store.remove(resolver.id)
    else store.put(resolver.id, resolver)

}

private object ResolverProjectCache {

  def apply[F[_]: Effect: Timer](
      project: ProjectRef
  )(implicit as: ActorSystem, config: KeyValueStoreConfig): ResolverProjectCache[F] =
    new ResolverProjectCache(KeyValueStore.distributed(s"resolver-${project.id}", (_, resolver) => resolver.rev))

}

object ResolverCache {

  def apply[F[_]: Effect: Timer](implicit as: ActorSystem, config: KeyValueStoreConfig): ResolverCache[F] =
    new ResolverCache(new ConcurrentHashMap[UUID, ResolverProjectCache[F]]())
} 
Example 87
Source File: StorageCache.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.cache

import java.time.{Clock, Instant}
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap

import akka.actor.ActorSystem
import cats.Monad
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.commons.cache.{KeyValueStore, KeyValueStoreConfig}
import ch.epfl.bluebrain.nexus.kg.RevisionedValue
import ch.epfl.bluebrain.nexus.kg.cache.Cache._
import ch.epfl.bluebrain.nexus.kg.cache.StorageProjectCache._
import ch.epfl.bluebrain.nexus.kg.resources.ProjectIdentifier.ProjectRef
import ch.epfl.bluebrain.nexus.kg.storage.Storage
import ch.epfl.bluebrain.nexus.rdf.Iri.AbsoluteIri

class StorageCache[F[_]: Effect: Timer] private (projectToCache: ConcurrentHashMap[UUID, StorageProjectCache[F]])(
    implicit as: ActorSystem,
    config: KeyValueStoreConfig,
    clock: Clock
) {

  
private class StorageProjectCache[F[_]: Monad] private (store: KeyValueStore[F, AbsoluteIri, RevisionedStorage])
    extends Cache[F, AbsoluteIri, RevisionedStorage](store) {

  private implicit val ordering: Ordering[RevisionedStorage] = Ordering.by((s: RevisionedStorage) => s.rev).reverse

  private implicit def revisioned(storage: Storage)(implicit instant: Instant): RevisionedStorage =
    RevisionedValue(instant.toEpochMilli, storage)

  def get: F[List[Storage]] =
    store.values.map(_.toList.sorted.map(_.value))

  def getDefault: F[Option[Storage]] =
    get.map(_.collectFirst { case storage if storage.default => storage })

  def getBy(id: AbsoluteIri): F[Option[Storage]] =
    get(id).map(_.collectFirst { case RevisionedValue(_, storage) if storage.id == id => storage })

  def put(storage: Storage)(implicit instant: Instant): F[Unit] =
    if (storage.deprecated) store.remove(storage.id)
    else store.put(storage.id, storage)
}

private object StorageProjectCache {

  type RevisionedStorage = RevisionedValue[Storage]

  def apply[F[_]: Effect: Timer](
      project: ProjectRef
  )(implicit as: ActorSystem, config: KeyValueStoreConfig): StorageProjectCache[F] =
    new StorageProjectCache(
      KeyValueStore.distributed(s"storage-${project.id}", (_, storage) => storage.value.rev)
    )

}

object StorageCache {

  def apply[F[_]: Timer: Effect](implicit as: ActorSystem, config: KeyValueStoreConfig, clock: Clock): StorageCache[F] =
    new StorageCache(new ConcurrentHashMap[UUID, StorageProjectCache[F]]())
} 
Example 88
Source File: AclsCache.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.cache

import akka.actor.ActorSystem
import cats.Monad
import cats.effect.{Effect, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.commons.cache.KeyValueStore
import ch.epfl.bluebrain.nexus.iam.client.IamClient
import ch.epfl.bluebrain.nexus.iam.client.types.events.Event._
import ch.epfl.bluebrain.nexus.iam.client.types.{AccessControlList, AccessControlLists, ResourceAccessControlList}
import ch.epfl.bluebrain.nexus.kg.config.AppConfig
import ch.epfl.bluebrain.nexus.kg.config.AppConfig._
import ch.epfl.bluebrain.nexus.kg.config.Vocabulary._
import ch.epfl.bluebrain.nexus.rdf.Iri.Path


  def apply[F[_]: Effect: Timer](
      iamClient: IamClient[F]
  )(implicit as: ActorSystem, config: AppConfig): AclsCache[F] = {
    val cache = new AclsCache(KeyValueStore.distributed("acls", (_, acls) => acls.rev))
    val handle: AclEvent => F[Unit] = {
      case event: AclReplaced   => cache.replace(event.path, toResourceAcl(event, event.acl))
      case event: AclAppended   => cache.append(event.path, toResourceAcl(event, event.acl))
      case event: AclSubtracted => cache.subtract(event.path, toResourceAcl(event, event.acl))
      case event: AclDeleted    => cache.remove(event.path)
    }
    iamClient.aclEvents(handle)(config.iam.serviceAccountToken)
    cache
  }
} 
Example 89
Source File: CatsInterop.scala    From caliban   with Apache License 2.0 5 votes vote down vote up
package caliban.interop.cats

import caliban.introspection.adt.__Type
import caliban.schema.Step.QueryStep
import caliban.schema.{ Schema, Step }
import caliban.{ CalibanError, GraphQL, GraphQLInterpreter, GraphQLResponse, InputValue }
import cats.effect.implicits._
import cats.effect.{ Async, Effect }
import zio.interop.catz._
import zio.{ Runtime, _ }
import zio.query.ZQuery

object CatsInterop {

  def executeAsync[F[_]: Async, R, E](graphQL: GraphQLInterpreter[R, E])(
    query: String,
    operationName: Option[String] = None,
    variables: Map[String, InputValue] = Map(),
    extensions: Map[String, InputValue] = Map(),
    skipValidation: Boolean = false,
    enableIntrospection: Boolean = true
  )(implicit runtime: Runtime[R]): F[GraphQLResponse[E]] =
    Async[F].async { cb =>
      val execution = graphQL.execute(
        query,
        operationName,
        variables,
        extensions,
        skipValidation = skipValidation,
        enableIntrospection = enableIntrospection
      )

      runtime.unsafeRunAsync(execution)(exit => cb(exit.toEither))
    }

  def checkAsync[F[_]: Async, R](
    graphQL: GraphQLInterpreter[R, Any]
  )(query: String)(implicit runtime: Runtime[R]): F[Unit] =
    Async[F].async(cb => runtime.unsafeRunAsync(graphQL.check(query))(exit => cb(exit.toEither)))

  def interpreterAsync[F[_]: Async, R](
    graphQL: GraphQL[R]
  )(implicit runtime: Runtime[R]): F[GraphQLInterpreter[R, CalibanError]] =
    Async[F].async(cb => runtime.unsafeRunAsync(graphQL.interpreter)(exit => cb(exit.toEither)))

  def schema[F[_]: Effect, R, A](implicit ev: Schema[R, A]): Schema[R, F[A]] =
    new Schema[R, F[A]] {
      override def toType(isInput: Boolean, isSubscription: Boolean): __Type =
        ev.toType(isInput, isSubscription)

      override def optional: Boolean =
        ev.optional

      override def resolve(value: F[A]): Step[R] =
        QueryStep(ZQuery.fromEffect(value.toIO.to[Task].map(ev.resolve)))
    }
} 
Example 90
Source File: package.scala    From caliban   with Apache License 2.0 5 votes vote down vote up
package caliban.interop.cats

import caliban.schema.Schema
import caliban.{ CalibanError, GraphQL, GraphQLInterpreter, GraphQLResponse, InputValue }
import cats.effect.{ Async, Effect }
import zio.Runtime

package object implicits {

  implicit class CatsEffectGraphQLInterpreter[R, E](underlying: GraphQLInterpreter[R, E]) {

    def executeAsync[F[_]: Async](
      query: String,
      operationName: Option[String] = None,
      variables: Map[String, InputValue] = Map(),
      extensions: Map[String, InputValue] = Map(),
      skipValidation: Boolean = false,
      enableIntrospection: Boolean = true
    )(implicit runtime: Runtime[R]): F[GraphQLResponse[E]] =
      CatsInterop.executeAsync(underlying)(
        query,
        operationName,
        variables,
        extensions,
        skipValidation = skipValidation,
        enableIntrospection = enableIntrospection
      )

    def checkAsync[F[_]: Async](query: String)(implicit runtime: Runtime[R]): F[Unit] =
      CatsInterop.checkAsync(underlying)(query)
  }

  implicit class CatsEffectGraphQL[R, E](underlying: GraphQL[R]) {

    def interpreterAsync[F[_]: Async](implicit runtime: Runtime[R]): F[GraphQLInterpreter[R, CalibanError]] =
      CatsInterop.interpreterAsync(underlying)
  }

  implicit def effectSchema[F[_]: Effect, R, A](implicit ev: Schema[R, A]): Schema[R, F[A]] =
    CatsInterop.schema
} 
Example 91
Source File: package.scala    From interop-cats   with Apache License 2.0 5 votes vote down vote up
package zio

import cats.effect.{ Effect, ExitCase, LiftIO }
import zio.interop.catz.taskEffectInstance

package object interop {
  type ParIO[-R, +E, +A] = Par.T[R, E, A]

  type Queue[F[+_], A] = CQueue[F, A, A]

  @inline private[interop] final def exitToExitCase(exit: Exit[Any, Any]): ExitCase[Throwable] = exit match {
    case Exit.Success(_)                          => ExitCase.Completed
    case Exit.Failure(cause) if cause.interrupted => ExitCase.Canceled
    case Exit.Failure(cause) =>
      cause.failureOrCause match {
        case Left(t: Throwable) => ExitCase.Error(t)
        case _                  => ExitCase.Error(FiberFailure(cause))
      }
  }

  @inline private[interop] final def exitCaseToExit[E](exitCase: ExitCase[E]): Exit[E, Unit] = exitCase match {
    case ExitCase.Completed => Exit.unit
    case ExitCase.Error(e)  => Exit.fail(e)
    case ExitCase.Canceled  => Exit.interrupt(Fiber.Id.None)
  }

  private[interop] def fromEffect[F[+_], R, A](
    eff: F[A]
  )(implicit R: Runtime[R], F: Effect[F]): RIO[R, A] =
    taskEffectInstance.liftIO[A](F.toIO(eff))

  private[interop] def toEffect[F[+_], R, A](zio: RIO[R, A])(implicit R: Runtime[R], F: LiftIO[F]): F[A] =
    F.liftIO(taskEffectInstance.toIO(zio))
} 
Example 92
Source File: MicrometerHttp4sMetricsOpsModule.scala    From scala-server-toolkit   with MIT License 5 votes vote down vote up
package com.avast.sst.http4s.server.micrometer

import java.util.concurrent.TimeUnit

import cats.effect.Effect
import cats.effect.concurrent.Ref
import cats.syntax.functor._
import io.micrometer.core.instrument.MeterRegistry
import org.http4s.metrics.{MetricsOps, TerminationType}
import org.http4s.{Method, Status}

object MicrometerHttp4sMetricsOpsModule {

  
  def make[F[_]: Effect](meterRegistry: MeterRegistry): F[MetricsOps[F]] = {
    val F = Effect[F]

    for {
      activeRequests <- Ref.of[F, Long](0L)
    } yield new MetricsOps[F] {

      private val prefix = "http.global"
      private val failureTime = meterRegistry.timer(s"$prefix.failure-time")

      meterRegistry.gauge(
        s"$prefix.active-requests",
        activeRequests,
        (_: Ref[F, Long]) => Effect[F].toIO(activeRequests.get).unsafeRunSync().toDouble
      )

      override def increaseActiveRequests(classifier: Option[String]): F[Unit] = activeRequests.update(_ + 1)

      override def decreaseActiveRequests(classifier: Option[String]): F[Unit] = activeRequests.update(_ - 1)

      override def recordHeadersTime(method: Method, elapsed: Long, classifier: Option[String]): F[Unit] = {
        F.delay(meterRegistry.timer(s"$prefix.headers-time", "method", method.name).record(elapsed, TimeUnit.NANOSECONDS))
      }

      override def recordTotalTime(method: Method, status: Status, elapsed: Long, classifier: Option[String]): F[Unit] = {
        F.delay(
          meterRegistry
            .timer(s"$prefix.total-time", "status", s"${status.code}", "status-class", s"${status.code / 100}xx")
            .record(elapsed, TimeUnit.NANOSECONDS)
        )
      }

      override def recordAbnormalTermination(elapsed: Long, terminationType: TerminationType, classifier: Option[String]): F[Unit] = {
        F.delay(failureTime.record(elapsed, TimeUnit.NANOSECONDS))
      }

    }
  }

} 
Example 93
Source File: Decrypt.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.kms

import cats.effect.Effect
import io.circe.{Decoder, Json}
import org.aws4s.Region
import org.aws4s.core.Command.Validator
import org.aws4s.core.{CommandPayload, Param}

private[kms] case class Decrypt[F[_]: Effect](
    region:      Region,
    ciphertext:  Ciphertext,
    context:     Option[EncryptionContext],
    grantTokens: Option[GrantTokens],
) extends KmsCommand[F, DecryptSuccess] {

  override def action: String = "Decrypt"

  override def params: List[Param[Json]] =
    CommandPayload.params(ciphertext)(context, grantTokens)

  override val validator: Validator[Json] = _ => None
}

case class DecryptSuccess(
    plainText: Plaintext,
)

object DecryptSuccess {
  implicit val decoder: Decoder[DecryptSuccess] =
    Decoder.forProduct1(Plaintext.name) { (plaintext: Plaintext) =>
      DecryptSuccess(plaintext)
    }
} 
Example 94
Source File: KmsCommand.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.kms

import cats.effect.Effect
import cats.implicits._
import io.circe.{Decoder, Json}
import org.aws4s.core.ExtraEntityDecoderInstances._
import org.aws4s._
import org.aws4s.core.{Command, CommandPayload, RenderedParam, ServiceName}
import org.http4s.circe._
import org.http4s.headers.{Host, `Content-Type`}
import org.http4s.{Header, Headers, MediaType, Method, Request, Uri}

private[kms] abstract class KmsCommand[F[_]: Effect, R: Decoder] extends Command[F, Json, R] {
  override def serviceName:    ServiceName    = ServiceName.Kms
  override def payloadSigning: PayloadSigning = PayloadSigning.Signed

  def action: String

  override final val requestGenerator: List[RenderedParam[Json]] => F[Request[F]] = { params =>
    val host = s"kms.${region.name}.amazonaws.com"
    val payload: Json = CommandPayload.jsonObject(params)
    Request[F](
      Method.POST,
      Uri.unsafeFromString(s"https://$host/"),
      headers = Headers(Header("X-Amz-Target", s"TrentService.$action"), Host(host))
    ).withBody(payload).map(_.withContentType(`Content-Type`.apply(MediaType.fromKey(("application", "x-amz-json-1.1")))))
  }
} 
Example 95
Source File: Encrypt.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.kms

import cats.effect.Effect
import io.circe.{Decoder, Json}
import org.aws4s.Region
import org.aws4s.core.Command.Validator
import org.aws4s.core.{CommandPayload, Param}

private[kms] case class Encrypt[F[_]: Effect](
    region:      Region,
    keyId:       KeyId,
    plaintext:   Plaintext,
    context:     Option[EncryptionContext],
    grantTokens: Option[GrantTokens],
) extends KmsCommand[F, EncryptSuccess] {

  override def action: String = "Encrypt"

  override def params: List[Param[Json]] =
    CommandPayload.params(keyId, plaintext)(context, grantTokens)

  override val validator: Validator[Json] = _ => None
}

case class EncryptSuccess(
    cipherText: Ciphertext,
)

object EncryptSuccess {
  implicit val decoder: Decoder[EncryptSuccess] =
    Decoder.forProduct1(Ciphertext.name) { (cipherText: Ciphertext) =>
      EncryptSuccess(cipherText)
    }
} 
Example 96
Source File: CreateKey.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.kms

import cats.effect.Effect
import io.circe.{Decoder, Json}
import org.aws4s.Region
import org.aws4s.core.Command.Validator
import org.aws4s.core.{CommandPayload, Param}

private[kms] case class CreateKey[F[_]: Effect](
    region:      Region,
    description: Option[KeyDescription],
) extends KmsCommand[F, CreateKeySuccess] {

  override val action: String = "CreateKey"

  override def params: List[Param[Json]] = CommandPayload.params()(description)

  override val validator: Validator[Json] = _ => None
}

case class CreateKeySuccess(
    keyMetadata: KeyMetadata,
)

object CreateKeySuccess {
  implicit val decoder: Decoder[CreateKeySuccess] =
    Decoder.forProduct1("KeyMetadata")(CreateKeySuccess.apply)
} 
Example 97
Source File: Kms.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.kms

import cats.effect.Effect
import io.circe.Json
import org.aws4s.core.ExtraEntityDecoderInstances._
import org.aws4s.core.Service
import org.aws4s.{Credentials, Region}
import org.http4s.client.Client

case class Kms[F[_]: Effect](client: F[Client[F]], region: Region, credentials: () => Credentials) extends Service[F, Json] {

  def encrypt(
      keyId:       KeyId,
      plaintext:   Plaintext,
      context:     Option[EncryptionContext] = None,
      grantTokens: Option[GrantTokens] = None,
  ): F[EncryptSuccess] = run {
    Encrypt(
      region,
      keyId,
      plaintext,
      context,
      grantTokens
    )
  }

  def decrypt(
      ciphertext:  Ciphertext,
      context:     Option[EncryptionContext] = None,
      grantTokens: Option[GrantTokens] = None,
  ): F[DecryptSuccess] = run {
    Decrypt(region, ciphertext, context, grantTokens)
  }

  def createKey(description: Option[KeyDescription] = None): F[CreateKeySuccess] = run {
    CreateKey(region, description)
  }

  def scheduleKeyDeletion(keyId: KeyId, pendingWindowInDays: Option[PendingWindowInDays] = None): F[ScheduleKeyDeletionSuccess] = run {
    ScheduleKeyDeletion(region, keyId, pendingWindowInDays)
  }
} 
Example 98
Source File: ScheduleKeyDeletion.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.kms

import java.time.Instant
import cats.effect.Effect
import io.circe.{Decoder, Json}
import org.aws4s.Region
import org.aws4s.core.ExtraCirceDecoders._
import org.aws4s.core.Command.Validator
import org.aws4s.core.{CommandPayload, Param}

private[kms] case class ScheduleKeyDeletion[F[_]: Effect](
    region:              Region,
    keyId:               KeyId,
    pendingWindowInDays: Option[PendingWindowInDays],
) extends KmsCommand[F, ScheduleKeyDeletionSuccess] {
  override def action: String = "ScheduleKeyDeletion"

  override val validator: Validator[Json] = _ => None

  override def params: List[Param[Json]] = CommandPayload.params(keyId)(pendingWindowInDays)
}

case class ScheduleKeyDeletionSuccess(
    keyId:        KeyId,
    deletionDate: Instant,
)

object ScheduleKeyDeletionSuccess {
  implicit val decoder: Decoder[ScheduleKeyDeletionSuccess] =
    Decoder.forProduct2(
      KeyId.name,
      "DeletionDate"
    )(ScheduleKeyDeletionSuccess.apply)
} 
Example 99
Source File: ReceiveMessage.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.sqs

import cats.effect.Effect
import org.http4s.EntityDecoder
import cats.implicits._
import org.aws4s.core.{CommandPayload, ExtraEntityDecoderInstances, Param}

private[sqs] case class ReceiveMessage[F[_]: Effect](
    q:                       Queue,
    maxNumberOfMessages:     Option[MaxNumberOfMessages],
    visibilityTimeout:       Option[VisibilityTimeout],
    waitTimeSeconds:         Option[WaitTimeSeconds],
    receiveRequestAttemptId: Option[ReceiveRequestAttemptId],
) extends SqsCommand[F, ReceiveMessageSuccess] {

  override val action: String = "ReceiveMessage"

  override final val params: List[Param[String]] =
    CommandPayload.params()(
      maxNumberOfMessages,
      visibilityTimeout,
      waitTimeSeconds,
      receiveRequestAttemptId
    )
}

case class ReceiveMessageSuccess(
    messages: List[Message]
)

object ReceiveMessageSuccess {
  implicit def entityDecoder[F[_]: Effect]: EntityDecoder[F, ReceiveMessageSuccess] =
    ExtraEntityDecoderInstances.fromXml { elem =>
      if (elem.label == "ReceiveMessageResponse")
        (elem \ "ReceiveMessageResult" \ "Message").toList.traverse(Message.parse) map { messages =>
          ReceiveMessageSuccess(messages)
        } else None
    }
} 
Example 100
Source File: SqsCommand.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.sqs

import cats.effect.Effect
import org.http4s.headers.Host
import org.http4s.{EntityDecoder, Headers, Method, Request, UrlForm}
import org.aws4s._
import org.aws4s.core.Command.Validator
import org.aws4s.core.{Command, RenderedParam, ServiceName}

private[sqs] abstract class SqsCommand[F[_]: Effect, R: EntityDecoder[F, ?]] extends Command[F, String, R] {

  val q:      Queue
  val action: String

  override final val serviceName:    ServiceName    = ServiceName.Sqs
  override final val payloadSigning: PayloadSigning = PayloadSigning.Signed
  override final val region: Region = q.region

  override final val validator: Validator[String] = _ => None
  override final val requestGenerator: List[RenderedParam[String]] => F[Request[F]] = { params =>
    val body = params.map(p => (p.name, p.value)).foldLeft(UrlForm())((form, newPair) => form + newPair) + ("Action" -> action)
    Request[F](Method.POST, q.uri, headers = Headers(Host(q.host))).withBody[UrlForm](body)
  }
} 
Example 101
Source File: Sqs.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.sqs

import cats.effect.Effect
import org.aws4s.core.Service
import org.aws4s.Credentials
import org.http4s.client.Client

case class Sqs[F[_]: Effect](client: F[Client[F]], credentials: () => Credentials) extends Service[F, String] {

  def sendMessage(
      q:                      Queue,
      messageBody:            MessageBody,
      delaySeconds:           Option[DelaySeconds] = None,
      messageDeduplicationId: Option[MessageDeduplicationId] = None
  ): F[SendMessageSuccess] = run {
    SendMessage(
      q,
      messageBody,
      delaySeconds,
      messageDeduplicationId
    )
  }

  def receiveMessage(
      q:                       Queue,
      maxNumberOfMessages:     Option[MaxNumberOfMessages] = None,
      visibilityTimeout:       Option[VisibilityTimeout] = None,
      waitTimeSeconds:         Option[WaitTimeSeconds] = None,
      receiveRequestAttemptId: Option[ReceiveRequestAttemptId] = None,
  ): F[ReceiveMessageSuccess] = run {
    ReceiveMessage(
      q,
      maxNumberOfMessages,
      visibilityTimeout,
      waitTimeSeconds,
      receiveRequestAttemptId
    )
  }

  def deleteMessage(
      q:             Queue,
      receiptHandle: ReceiptHandle,
  ): F[Unit] = run {
    DeleteMessage(
      q,
      receiptHandle
    )
  }
} 
Example 102
Source File: SendMessage.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.sqs

import cats.effect.Effect
import org.http4s.EntityDecoder
import org.aws4s.core.XmlParsing._
import org.aws4s.core.{CommandPayload, ExtraEntityDecoderInstances, Param}

private[sqs] case class SendMessage[F[_]: Effect](
    q:                      Queue,
    messageBody:            MessageBody,
    delaySeconds:           Option[DelaySeconds] = None,
    messageDeduplicationId: Option[MessageDeduplicationId] = None,
) extends SqsCommand[F, SendMessageSuccess] {

  override val action: String = "SendMessage"

  override def params: List[Param[String]] =
    CommandPayload.params(
      messageBody
    )(
      delaySeconds,
      messageDeduplicationId
    )
}

case class SendMessageSuccess(
    messageId:        MessageId,
    md5OfMessageBody: String,
    sequenceNumber:   Option[SequenceNumber]
)

object SendMessageSuccess {
  implicit def entityDecoder[F[_]: Effect]: EntityDecoder[F, SendMessageSuccess] =
    ExtraEntityDecoderInstances.fromXml { elem =>
      if (elem.label == "SendMessageResponse")
        Some(
          SendMessageSuccess(
            MessageId(text(elem)("SendMessageResult", "MessageId")),
            text(elem)("SendMessageResult", "MD5OfMessageBody"),
            integer(elem)("SendMessageResult", "SequenceNumber") map SequenceNumber
          )
        )
      else
        None
    }
} 
Example 103
Source File: CreateTable.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.dynamodb

import cats.data.NonEmptyList
import cats.effect.Effect
import io.circe.{Decoder, Json}
import org.aws4s.Region
import org.aws4s.core.{Command, Param}

private[dynamodb] case class CreateTable[F[_]: Effect](
    region:                Region,
    indices:               NonEmptyList[Index],
    tableName:             TableName,
    provisionedThroughput: ProvisionedThroughput,
) extends DynamoDbCommand[F, CreateTableSuccess] {

  override def action: String = "CreateTable"

  override def params: List[Param[Json]] = {
    val attributeDefinitions = AttributeDefinitions(indices.map(ix => AttributeDefinition(ix.attributeName, ix.attributeType)))
    val keySchema            = KeySchema(indices.map(ix            => KeySchemaElement(ix.attributeName, ix.keyType)))

    List(attributeDefinitions, tableName, keySchema, provisionedThroughput)
  }

  override val validator: Command.Validator[Json] = _ => None
}

case class CreateTableSuccess(tableDescription: TableDescription)

object CreateTableSuccess {
  implicit val decoder: Decoder[CreateTableSuccess] =
    Decoder.forProduct1(TableDescription.name)(CreateTableSuccess.apply)
} 
Example 104
Source File: DynamoDbCommand.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.dynamodb

import cats.effect.Effect
import io.circe.{Decoder, Json}
import org.aws4s.PayloadSigning
import org.http4s.headers.{Host, `Content-Type`}
import org.http4s.{Header, Headers, MediaType, Method, Request, Uri}
import org.http4s.circe._
import cats.implicits._
import org.aws4s.core.ExtraEntityDecoderInstances._
import org.aws4s.core.{Command, CommandPayload, RenderedParam, ServiceName}

private[dynamodb] abstract class DynamoDbCommand[F[_]: Effect, R: Decoder] extends Command[F, Json, R] {
  override def serviceName:    ServiceName    = ServiceName.DynamoDb
  override def payloadSigning: PayloadSigning = PayloadSigning.Signed

  def action: String

  override final val requestGenerator: List[RenderedParam[Json]] => F[Request[F]] = params => {
    val host = s"dynamodb.${region.name}.amazonaws.com"
    val payload: Json = CommandPayload.jsonObject(params)
    Request[F](
      Method.POST,
      Uri.unsafeFromString(s"https://$host/"),
      headers = Headers(Header("X-Amz-Target", s"DynamoDB_20120810.$action"), Host(host))
    ).withBody(payload).map(_.withContentType(`Content-Type`.apply(MediaType.fromKey(("application", "x-amz-json-1.0")))))
  }
} 
Example 105
Source File: DynamoDb.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.dynamodb

import cats.data.NonEmptyList
import cats.effect.Effect
import io.circe.Json
import org.aws4s.core.Service
import org.aws4s.{Credentials, Region}
import org.http4s.client.Client
import org.aws4s.core.ExtraEntityDecoderInstances._

case class DynamoDb[F[_]: Effect](client: F[Client[F]], region: Region, credentials: () => Credentials) extends Service[F, Json] {

  def createTable(tableName: TableName, indices: NonEmptyList[Index], provisionedThroughput: ProvisionedThroughput): F[CreateTableSuccess] =
    run {
      CreateTable(region, indices, tableName, provisionedThroughput)
    }

  def deleteTable(tableName: TableName): F[DeleteTableSuccess] =
    run {
      DeleteTable(region, tableName)
    }
} 
Example 106
Source File: S3ObjectCommand.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.s3

import cats.effect.Effect
import org.aws4s.core.{Command, Param, RenderedParam, ServiceName}
import org.http4s.{EntityDecoder, Headers, Method, Request, Uri}
import fs2._
import org.http4s.headers.Host
import cats.implicits._

private[aws4s] abstract class S3ObjectCommand[F[_]: Effect, R: EntityDecoder[F, ?]] extends Command[F, Nothing, R] {

  override final val serviceName: ServiceName = ServiceName.S3

  val action:     Method
  val bucketName: BucketName
  val objectPath: ObjectPath
  val payload:    F[Stream[F, Byte]]

  override final val params:    List[Param[Nothing]]       = List.empty
  override final val validator: Command.Validator[Nothing] = _ => None

  override final val requestGenerator: List[RenderedParam[Nothing]] => F[Request[F]] = { _ =>
    val host = s"${bucketName.value}.s3.${region.name}.amazonaws.com"
    val uri  = Uri.unsafeFromString(s"https://$host/").withPath(objectPath.value)
    for {
      pStream <- payload
      pBytes <- pStream.compile.toVector
      r <- Request[F](action, uri, headers = Headers(Host(host))).withBody(pBytes.toArray)
    } yield r
  }
} 
Example 107
Source File: S3.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.s3

import cats.effect.Effect
import org.aws4s._
import org.http4s.client.Client
import fs2.Stream
import org.aws4s.core.ExtraEntityDecoderInstances._
import org.aws4s.core.Service

case class S3[F[_]: Effect](client: F[Client[F]], region: Region, credentials: () => Credentials) extends Service[F, Nothing] {

  val listBuckets: F[ListBucketsSuccess] = run {
    ListBuckets(region)
  }

  def putObject(
      bucket:         BucketName,
      objectPath:     ObjectPath,
      obj:            F[Stream[F, Byte]],
      payloadSigning: PayloadSigning = PayloadSigning.Unsigned,
  ): F[Unit] = run {
    PutObject(region, bucket, objectPath, obj, payloadSigning)
  }

  def deleteObject(
      bucket:     BucketName,
      objectPath: ObjectPath
  ): F[Unit] = run {
    DeleteObject(region, bucket, objectPath)
  }

  def getObject(
      bucket:     BucketName,
      objectPath: ObjectPath
  ): F[Stream[F, Byte]] = run {
    GetObject(region, bucket, objectPath)
  }
} 
Example 108
Source File: PutObject.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.s3

import cats.effect.Effect
import org.aws4s.{PayloadSigning, _}
import org.http4s.Method
import fs2.Stream

private[aws4s] case class PutObject[F[_]: Effect](
    region:         Region,
    bucketName:     BucketName,
    objectPath:     ObjectPath,
    obj:            F[Stream[F, Byte]],
    payloadSigning: PayloadSigning
) extends S3ObjectCommand[F, Unit] {

  override val action:  Method             = Method.PUT
  override val payload: F[Stream[F, Byte]] = obj
} 
Example 109
Source File: S3ServiceCommand.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.s3

import cats.effect.Effect
import cats.implicits._
import fs2._
import org.aws4s.PayloadSigning
import org.aws4s.core.{Command, Param, RenderedParam, ServiceName}
import org.http4s.headers.Host
import org.http4s.{EntityDecoder, Headers, Method, Request, Uri}

private[aws4s] abstract class S3ServiceCommand[F[_]: Effect, R: EntityDecoder[F, ?]] extends Command[F, Nothing, R] {

  override final val serviceName: ServiceName = ServiceName.S3

  val action:  Method
  val payload: F[Stream[F, Byte]]

  override final val payloadSigning: PayloadSigning              = PayloadSigning.Signed
  override final val params:         List[Param[Nothing]]       = List.empty
  override final val validator:      Command.Validator[Nothing] = _ => None

  override final val requestGenerator: List[RenderedParam[Nothing]] => F[Request[F]] = { _ =>
    val host = s"s3.${region.name}.amazonaws.com"
    val uri  = Uri.unsafeFromString(s"https://$host/").withPath("/")
    payload map { p =>
      Request[F](action, uri, headers = Headers(Host(host))).withBodyStream(p)
    }
  }
} 
Example 110
Source File: ListBuckets.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.s3

import cats.effect.Effect
import org.aws4s._
import org.http4s.{EntityDecoder, Method}
import cats.implicits._
import fs2.Stream
import org.aws4s.core.ExtraEntityDecoderInstances

private[s3] case class ListBuckets[F[_]: Effect](
    region: Region
) extends S3ServiceCommand[F, ListBucketsSuccess] {

  override val action:  Method             = Method.GET
  override val payload: F[Stream[F, Byte]] = (Stream.empty: Stream[F, Byte]).pure[F]
}

case class ListBucketsSuccess(
    buckets: List[BucketName]
)

object ListBucketsSuccess {
  implicit def entityDecoder[F[_]: Effect]: EntityDecoder[F, ListBucketsSuccess] =
    ExtraEntityDecoderInstances.fromXml { elem =>
      if (elem.label == "ListAllMyBucketsResult")
        (elem \ "Buckets" \ "Bucket").toList.traverse(BucketName.parse) map { buckets =>
          ListBucketsSuccess(buckets)
        } else None
    }
} 
Example 111
Source File: ExtraEntityDecoderInstances.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.core

import cats.Applicative
import cats.data.EitherT
import cats.effect.Effect
import cats.implicits._
import fs2.Stream
import io.circe.Decoder
import org.http4s.scalaxml._
import org.http4s.{DecodeFailure, EntityDecoder, InvalidMessageBodyFailure, MediaRange}

private[aws4s] object ExtraEntityDecoderInstances {
  implicit def streamEntityDecoder[F[_]: Applicative]: EntityDecoder[F, Stream[F, Byte]] =
    EntityDecoder.decodeBy(MediaRange.`*/*`) { msg =>
      EitherT.fromEither(msg.body.asRight[DecodeFailure])
    }

  def fromXml[F[_]: Effect, A](f: scala.xml.Elem => Option[A]): EntityDecoder[F, A] =
    EntityDecoder[F, scala.xml.Elem] flatMapR { elem =>
      val result = f(elem)
      EitherT.fromEither(result.toRight(InvalidMessageBodyFailure("Response was not as expected")))
    }

  implicit def circeEntityDecoder[F[_]: Effect, A: Decoder]: EntityDecoder[F, A] =
    org.http4s.circe.jsonOf[F, A]
} 
Example 112
Source File: ResponseContent.scala    From aws4s   with MIT License 5 votes vote down vote up
package org.aws4s.core

import cats.data.NonEmptyList
import cats.effect.{Effect, Sync}
import io.circe.Json
import org.http4s.{EntityDecoder, MediaRange, Message}
import org.http4s.scalaxml._
import org.http4s.circe._

private[aws4s] sealed trait ResponseContent {
  final def tryParse[A](pf: PartialFunction[ResponseContent, Option[A]]): Option[A] =
    pf.orElse[ResponseContent, Option[A]]({ case _ => None })(this)
}

private[aws4s] case class XmlContent(elem:    scala.xml.Elem) extends ResponseContent
private[aws4s] case class JsonContent(json:   Json) extends ResponseContent
private[aws4s] case class StringContent(text: String) extends ResponseContent
private[aws4s] case object NoContent extends ResponseContent

private[aws4s] object ResponseContent {

  implicit def entityDecoder[F[_]: Effect]: EntityDecoder[F, ResponseContent] =
    EntityDecoder[F, scala.xml.Elem].map(elem => XmlContent(elem)).widen[ResponseContent] orElse
      EntityDecoder[F, Json].map(json         => JsonContent(json)).widen[ResponseContent] orElse
      inclusiveJsonEntityDecoder.map(json     => JsonContent(json)).widen[ResponseContent] orElse
      EntityDecoder[F, String].map(text       => StringContent(text)).widen[ResponseContent] orElse
      EntityDecoder[F, Unit].map(_            => NoContent).widen[ResponseContent]

  private def inclusiveJsonEntityDecoder[F[_]: Sync]: EntityDecoder[F, Json] = {
    val json = jsonDecoder[F]
    val extraMediaRanges = NonEmptyList.of(
      "application/x-amz-json-1.0"
    ) map (mr => MediaRange.parse(mr).getOrElse(throw new RuntimeException(s"Invalid Media Range: $mr")))
    val allMediaRanges = extraMediaRanges concat json.consumes.toList
    EntityDecoder.decodeBy[F, Json](allMediaRanges.head, allMediaRanges.tail: _*)((msg: Message[F]) => json.decode(msg, strict = false))
  }
} 
Example 113
Source File: Fs2OutputStream.scala    From fs2-blobstore   with Apache License 2.0 4 votes vote down vote up
package blobstore.gcs

import java.io.OutputStream

import cats.effect.{ConcurrentEffect, Effect}
import fs2.{Chunk, Stream}
import fs2.concurrent.Queue
import cats.syntax.functor._

import scala.annotation.tailrec

private[gcs] class Fs2OutputStream[F[_]](
  queue: Queue[F, Option[Chunk[Byte]]],
  chunkSize: Int
)(implicit eff: Effect[F])
  extends OutputStream {
  @SuppressWarnings(Array("scalafix:DisableSyntax.var"))
  private var bufferedChunk: Chunk[Byte] = Chunk.empty

  @tailrec
  private def addChunk(newChunk: Chunk[Byte]): Unit = {
    val newChunkSize         = newChunk.size
    val bufferedChunkSize    = bufferedChunk.size
    val spaceLeftInTheBuffer = chunkSize - bufferedChunkSize

    if (newChunkSize > spaceLeftInTheBuffer) {
      bufferedChunk = Chunk.concatBytes(Seq(bufferedChunk, newChunk.take(spaceLeftInTheBuffer)), chunkSize)
      flushBuffer()
      addChunk(newChunk.drop(spaceLeftInTheBuffer))
    } else {
      bufferedChunk =
        Chunk.concatBytes(Seq(bufferedChunk, newChunk), bufferedChunkSize + newChunkSize)
    }
  }

  private def flushBuffer(): Unit = {
    enqueueChunkSync(Some(bufferedChunk))
    bufferedChunk = Chunk.empty
  }

  private def enqueueChunkSync(c: Option[Chunk[Byte]]): Unit =
    eff.toIO(queue.enqueue1(c)).unsafeRunSync()

  val stream: Stream[F, Byte] = queue.dequeue.unNoneTerminate.flatMap(Stream.chunk)

  override def write(bytes: Array[Byte]): Unit =
    addChunk(Chunk.bytes(bytes))
  override def write(bytes: Array[Byte], off: Int, len: Int): Unit =
    addChunk(Chunk.bytes(bytes, off, len))
  override def write(b: Int): Unit =
    addChunk(Chunk.singleton(b.toByte))

  override def flush(): Unit = flushBuffer()

  override def close(): Unit = {
    flush()
    enqueueChunkSync(None)
  }
}

private[gcs] object Fs2OutputStream {
  def apply[F[_]: ConcurrentEffect](chunkSize: Int, queueSize: Option[Int]): F[Fs2OutputStream[F]] = {
    val fQueue = queueSize match {
      case None       => Queue.unbounded[F, Option[Chunk[Byte]]]
      case Some(size) => Queue.bounded[F, Option[Chunk[Byte]]](size)
    }
    fQueue.map { queue => new Fs2OutputStream[F](queue, chunkSize) }
  }
}