monix.eval.Task Scala Examples

The following examples show how to use monix.eval.Task. 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: NoOpHttpClient.scala    From cornichon   with Apache License 2.0 6 votes vote down vote up
package com.github.agourlay.cornichon.http.client

import cats.Show
import cats.data.EitherT
import cats.syntax.either._
import com.github.agourlay.cornichon.core.Done
import com.github.agourlay.cornichon.http.{ CornichonHttpResponse, HttpRequest, HttpStreamedRequest }
import monix.eval.Task
import org.http4s.EntityEncoder

import scala.concurrent.duration.FiniteDuration

class NoOpHttpClient extends HttpClient {

  def runRequest[A: Show](cReq: HttpRequest[A], t: FiniteDuration)(implicit ee: EntityEncoder[Task, A]) =
    EitherT.apply(Task.now(CornichonHttpResponse(200, Nil, "NoOpBody").asRight))

  def openStream(req: HttpStreamedRequest, t: FiniteDuration) =
    Task.now(CornichonHttpResponse(200, Nil, "NoOpBody").asRight)

  def shutdown() =
    Done.taskDone

  def paramsFromUrl(url: String) =
    Right(Nil)
} 
Example 2
Source File: MockHttpServer.scala    From cornichon   with Apache License 2.0 6 votes vote down vote up
package com.github.agourlay.cornichon.http.server

import java.net.NetworkInterface

import com.github.agourlay.cornichon.core.CornichonError
import monix.eval.Task
import monix.execution.Scheduler
import org.http4s.HttpRoutes
import org.http4s.server.Router
import org.http4s.server.blaze.BlazeServerBuilder
import org.http4s.implicits._

import scala.jdk.CollectionConverters._
import scala.concurrent.duration._
import scala.util.Random

class MockHttpServer[A](interface: Option[String], port: Option[Range], mockService: HttpRoutes[Task], maxRetries: Int = 5)(useFromAddress: String => Task[A])(implicit scheduler: Scheduler) {

  private val selectedInterface = interface.getOrElse(bestInterface())
  private val randomPortOrder = port.fold(0 :: Nil)(r => Random.shuffle(r.toList))

  private val mockRouter = Router("/" -> mockService).orNotFound

  def useServer(): Task[A] =
    if (randomPortOrder.isEmpty)
      Task.raiseError(MockHttpServerError.toException)
    else
      startServerTryPorts(randomPortOrder)

  private def startServerTryPorts(ports: List[Int], retry: Int = 0): Task[A] =
    startBlazeServer(ports.head).onErrorHandleWith {
      case _: java.net.BindException if ports.length > 1 =>
        startServerTryPorts(ports.tail, retry)
      case _: java.net.BindException if retry < maxRetries =>
        val sleepFor = retry + 1
        println(s"Could not start server on any port. Retrying in $sleepFor seconds...")
        startServerTryPorts(randomPortOrder, retry = retry + 1).delayExecution(sleepFor.seconds)
    }

  private def startBlazeServer(port: Int): Task[A] =
    BlazeServerBuilder[Task](executionContext = scheduler)
      .bindHttp(port, selectedInterface)
      .withoutBanner
      .withHttpApp(mockRouter)
      .withNio2(true)
      .resource
      .use(server => useFromAddress(s"http://${server.address.getHostString}:${server.address.getPort}"))

  private def bestInterface(): String =
    NetworkInterface.getNetworkInterfaces.asScala
      .filter(_.isUp)
      .flatMap(_.getInetAddresses.asScala)
      .find(_.isSiteLocalAddress)
      .map(_.getHostAddress)
      .getOrElse("localhost")
}

case object MockHttpServerError extends CornichonError {
  val baseErrorMessage = "the range of ports provided for the HTTP mock is invalid"
} 
Example 3
Source File: TaskLimiter.scala    From spark-tools   with Apache License 2.0 5 votes vote down vote up
package io.univalence.centrifuge.util

//from https://gist.github.com/alexandru/623fe6c587d73e89a8f14de284ca1e2d

import monix.eval.Task
import java.util.concurrent.TimeUnit
import scala.concurrent.duration._


  final case class State(window: Long, period: TimeUnit, requested: Int, limit: Int) {
    private def periodMillis =
      TimeUnit.MILLISECONDS.convert(1, period)

    def request(now: Timestamp): (Option[FiniteDuration], State) = {
      val periodMillis  = this.periodMillis
      val currentWindow = now / periodMillis

      if (currentWindow != window)
        (None, copy(window = currentWindow, requested = 1))
      else if (requested < limit)
        (None, copy(requested = requested + 1))
      else {
        val nextTS = (currentWindow + 1) * periodMillis
        val sleep  = nextTS - now
        (Some(sleep.millis), this)
      }
    }
  }
} 
Example 4
Source File: TwitterSearcher.scala    From gbf-raidfinder   with MIT License 5 votes vote down vote up
package walfie.gbf.raidfinder

import monix.eval.Task
import monix.reactive._
import scala.collection.JavaConverters._
import scala.concurrent.duration._
import scala.concurrent.Future
import twitter4j._
import walfie.gbf.raidfinder.TwitterSearcher.PaginationType
import walfie.gbf.raidfinder.util.BlockingIO

trait TwitterSearcher {
  import TwitterSearcher.TweetId

  def observable(
    searchTerm:   String,
    initialTweet: Option[TweetId],
    maxCount:     Int
  ): Observable[Seq[Status]]
}

object TwitterSearcher {
  sealed trait PaginationType
  case object Chronological extends PaginationType
  case object ReverseChronological extends PaginationType

  type TweetId = Long

  
  def observable(searchTerm: String, initialTweet: Option[TweetId], maxCount: Int): Observable[Seq[Status]] = {
    Observable.fromAsyncStateAction[Option[TweetId], Seq[Status]] { tweetId =>
      val query = new Query(searchTerm).count(maxCount)

      Task.fromFuture(searchFunction(query, tweetId, maxCount))
        .onErrorHandle { error: Throwable =>
          System.err.println(error) // TODO: Better handling?
          Seq.empty[Status] -> tweetId
        }
    }(None)
  }

  private val searchFunction = paginationType match {
    case Chronological => searchChronological _
    case ReverseChronological => searchReverseChronological _
  }

  private def searchChronological(
    query:        Query,
    initialTweet: Option[TweetId],
    maxCount:     Int
  ): Future[(Seq[Status], Option[TweetId])] = {
    initialTweet.foreach(query.setSinceId)

    BlockingIO.future {
      val queryResult = twitter.search(query)
      val tweetsEarliestFirst = queryResult.getTweets.asScala.sortBy(_.getCreatedAt) // Earliest first
      tweetsEarliestFirst -> Option(queryResult.getMaxId)
    }
  }

  private def searchReverseChronological(
    query:        Query,
    initialTweet: Option[TweetId],
    maxCount:     Int
  ): Future[(Seq[Status], Option[TweetId])] = {
    initialTweet.foreach(query.setMaxId)

    BlockingIO.future {
      val queryResult = twitter.search(query)
      val tweetsLatestFirst = queryResult.getTweets.asScala.sortBy(-_.getCreatedAt.getTime) // Latest first
      tweetsLatestFirst -> tweetsLatestFirst.lastOption.map(_.getId) // queryResult.getSinceId returns 0
    }
  }
} 
Example 5
Source File: RaidFinder.scala    From gbf-raidfinder   with MIT License 5 votes vote down vote up
package walfie.gbf.raidfinder

import java.util.Date
import monix.eval.Task
import monix.execution.{Cancelable, Scheduler}
import monix.reactive._
import scala.concurrent.duration._
import scala.concurrent.Future
import twitter4j._
import walfie.gbf.raidfinder.domain._
import walfie.gbf.raidfinder.util.CachedObservablesPartitioner

trait RaidFinder[T] {
  def getRaidTweets(bossName: BossName): Observable[T]
  def newBossObservable: Observable[RaidBoss]
  def getKnownBosses(): Map[BossName, RaidBoss]
  def purgeOldBosses(
    minDate:        Date,
    levelThreshold: Option[Int]
  ): Future[Map[BossName, RaidBoss]]

  def shutdown(): Unit
}

object RaidFinder {
  val DefaultCacheSizePerBoss = 20
  val DefaultBackfillSize = 200

  
  protected def onShutdown(): Unit = ()

  // TODO: Parsing happens twice somewhere -- should figure out where
  private val raidInfos = statusesObservable
    .collect(Function.unlift(StatusParser.parse))
    .publish

  private val (partitioner, partitionerCancelable) =
    CachedObservablesPartitioner.fromUngroupedObservable(
      raidInfos.map(_.tweet),
      cachedTweetsPerBoss,
      (_: RaidTweet).bossName,
      fromRaidTweet.from // TODO
    )

  private val (knownBosses, knownBossesCancelable) = KnownBossesObserver
    .fromRaidInfoObservable(raidInfos, initialBosses)

  val newBossObservable = knownBosses.newBossObservable

  private val raidInfosCancelable = raidInfos.connect()

  private val cancelable = Cancelable { () =>
    List(
      raidInfosCancelable,
      partitionerCancelable,
      knownBossesCancelable
    ).foreach(_.cancel)
    onShutdown()
  }

  def shutdown(): Unit = cancelable.cancel()
  def getKnownBosses(): Map[BossName, RaidBoss] =
    knownBosses.get()
  def getRaidTweets(bossName: BossName): Observable[T] =
    partitioner.getObservable(bossName)

  def purgeOldBosses(
    minDate:        Date,
    levelThreshold: Option[Int]
  ): Future[Map[BossName, RaidBoss]] =
    knownBosses.purgeOldBosses(minDate, levelThreshold)
} 
Example 6
Source File: ObservableSpec.scala    From gbf-raidfinder   with MIT License 5 votes vote down vote up
package walfie.gbf.raidfinder.util

import monix.eval.Task
import monix.execution.Ack
import monix.execution.schedulers.TestScheduler
import monix.reactive.{Observable, Observer}
import org.scalatest._
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.Matchers._
import scala.concurrent.Future

class ObservableSpec extends FreeSpec with ScalaFutures {
  case class Item(id: Int)
  object ItemRepository {
    def getItems(count: Int, pageNum: Int): Future[Seq[Item]] =
      Future.successful {
        (0 until count).map(i => Item(pageNum * count + i))
      }
  }

  // This was added as [[ObservableUtil.fromAsyncStateAction]] before
  // [[Observable.fromAsyncStateAction]] existed in Monix. Keeping these
  // tests around because why not.
  "fromAsyncStateAction" - {
    implicit val scheduler = TestScheduler()

    "yield an observable" in {

      val itemsPerPage = 5

      val observable = Observable.fromAsyncStateAction { pageNum: Int =>
        val nextPage = pageNum + 1
        val itemsF = ItemRepository.getItems(itemsPerPage, pageNum)
        Task.fromFuture(itemsF).map(_ -> nextPage)
      }(0)

      val resultF = observable.take(3).toListL.runAsync
      scheduler.tick()

      resultF.futureValue shouldBe Seq(
        (0 to 4).map(Item.apply),
        (5 to 9).map(Item.apply),
        (10 to 14).map(Item.apply)
      )
    }

    "stop on error" in {
      implicit val scheduler = TestScheduler()

      // Create an observable counter that errors when it gets to 5
      val error = new RuntimeException("Oh no!")
      val observable = Observable
        .fromAsyncStateAction[Int, Int] { counter: Int =>
          Task.fromFuture {
            if (counter == 5) Future.failed(error)
            else Future.successful(counter -> (counter + 1))
          }
        }(0)

      val observer = new TestObserver[Int]

      observable.take(10).subscribe(observer)
      scheduler.tick()

      observer.received shouldBe (0 to 4)
    }
  }
} 
Example 7
Source File: NTP.scala    From matcher   with MIT License 5 votes vote down vote up
package com.wavesplatform.dex.time

import java.net.{InetAddress, SocketTimeoutException}
import java.util.concurrent.RejectedExecutionException
import java.util.concurrent.atomic.AtomicBoolean

import com.wavesplatform.dex.domain.utils.ScorexLogging
import monix.eval.Task
import monix.execution.schedulers.SchedulerService
import monix.execution.{ExecutionModel, Scheduler}
import org.apache.commons.net.ntp.NTPUDPClient

import scala.concurrent.duration.DurationInt

class NTP(ntpServer: String) extends Time with ScorexLogging with AutoCloseable {

  private val offsetPanicThreshold = 1000000L
  private val ExpirationTimeout    = 60.seconds
  private val RetryDelay           = 10.seconds
  private val ResponseTimeout      = 10.seconds

  private val duringShutdown = new AtomicBoolean(false)

  private implicit val scheduler: SchedulerService = Scheduler.singleThread(
    name = "time-impl",
    daemonic = false,
    executionModel = ExecutionModel.AlwaysAsyncExecution,
    reporter = {
      case _: RejectedExecutionException if duringShutdown.get() => // ignore
      case e: Throwable                                          => log.error("An uncaught error", e)
    }
  )

  private val client = new NTPUDPClient()
  client.setDefaultTimeout(ResponseTimeout.toMillis.toInt)

  @volatile private var offset = 0L
  private val updateTask: Task[Unit] = {
    def newOffsetTask: Task[Option[(InetAddress, java.lang.Long)]] = Task {
      try {
        val info = client.getTime(InetAddress.getByName(ntpServer))
        info.computeDetails()
        Option(info.getOffset).map { offset =>
          val r = if (Math.abs(offset) > offsetPanicThreshold) throw new Exception("Offset is suspiciously large") else offset
          (info.getAddress, r)
        }
      } catch {
        case _: SocketTimeoutException =>
          None
        case t: Throwable =>
          log.warn("Problems with NTP: ", t)
          None
      }
    }

    newOffsetTask.flatMap {
      case None if !scheduler.isShutdown => updateTask.delayExecution(RetryDelay)
      case Some((server, newOffset)) if !scheduler.isShutdown =>
        log.trace(s"Adjusting time with $newOffset milliseconds, source: ${server.getHostAddress}.")
        offset = newOffset
        updateTask.delayExecution(ExpirationTimeout)
      case _ => Task.unit
    }
  }

  def correctedTime(): Long = System.currentTimeMillis() + offset

  private var txTime: Long = 0

  def getTimestamp(): Long = {
    txTime = Math.max(correctedTime(), txTime + 1)
    txTime
  }

  private val taskHandle = updateTask.runAsync {
    case Left(e) => log.error(s"Error executing task", e)
    case _       =>
  }

  override def close(): Unit = if (duringShutdown.compareAndSet(false, true)) {
    log.info("Shutting down Time")
    taskHandle.cancel()
    if (client.isOpen) client.close()
    scheduler.shutdown()
  }
} 
Example 8
Source File: ScorexLogging.scala    From matcher   with MIT License 5 votes vote down vote up
package com.wavesplatform.dex.domain.utils

import monix.eval.Task
import monix.execution.{CancelableFuture, Scheduler}
import org.slf4j.{Logger, LoggerFactory}

case class LoggerFacade(logger: Logger) {

  def trace(message: => String): Unit                       = if (logger.isTraceEnabled) logger.trace(message)
  def debug(message: => String, arg: Any): Unit             = if (logger.isDebugEnabled) logger.debug(message, arg)
  def debug(message: => String): Unit                       = if (logger.isDebugEnabled) logger.debug(message)
  def info(message: => String): Unit                        = if (logger.isInfoEnabled) logger.info(message)
  def info(message: => String, arg: Any): Unit              = if (logger.isInfoEnabled) logger.info(message, arg)
  def info(message: => String, throwable: Throwable): Unit  = if (logger.isInfoEnabled) logger.info(message, throwable)
  def warn(message: => String): Unit                        = if (logger.isWarnEnabled) logger.warn(message)
  def warn(message: => String, throwable: Throwable): Unit  = if (logger.isWarnEnabled) logger.warn(message, throwable)
  def error(message: => String): Unit                       = if (logger.isErrorEnabled) logger.error(message)
  def error(message: => String, throwable: Throwable): Unit = if (logger.isErrorEnabled) logger.error(message, throwable)
}

trait ScorexLogging {

  protected lazy val log: LoggerFacade = LoggerFacade(LoggerFactory.getLogger(this.getClass))

  implicit class TaskExt[A](t: Task[A]) {

    def runAsyncLogErr(implicit s: Scheduler): CancelableFuture[A] = logErr.runToFuture(s)

    def logErr: Task[A] = t.onErrorHandleWith { ex =>
      log.error(s"Error executing task", ex)
      Task.raiseError[A](ex)
    }
  }
} 
Example 9
Source File: MonadlessTaskSpec.scala    From monadless   with Apache License 2.0 5 votes vote down vote up
package io.monadless.monix

import java.util.concurrent.TimeUnit

import org.scalatest.MustMatchers

import io.monadless.impl.TestSupport
import monix.eval.Task
import monix.execution.Cancelable
import monix.execution.schedulers.ReferenceScheduler

class MonadlessTaskSpec
  extends org.scalatest.FreeSpec
  with MustMatchers
  with MonadlessTask
  with TestSupport[Task] {

  implicit val s = new ReferenceScheduler {
    def scheduleOnce(initialDelay: Long, unit: TimeUnit, r: Runnable) = {
      r.run()
      Cancelable.empty
    }
    def execute(command: Runnable) = command.run()
    def executionModel = monix.execution.ExecutionModel.SynchronousExecution
    def reportFailure(t: Throwable): Unit = {}
  }

  def get[T](f: Task[T]) =
    f.runSyncMaybe.right.get

  def fail[T]: T = throw new Exception

  val one = Task(1)
  val two = Task(2)

  "apply" in
    runLiftTest(1) {
      1
    }

  "collect" in
    runLiftTest(3) {
      unlift(one) + unlift(two)
    }

  "map" in
    runLiftTest(2) {
      unlift(one) + 1
    }

  "flatMap" in
    runLiftTest(3) {
      val a = unlift(one)
      a + unlift(two)
    }

  "rescue" - {
    "success" in
      runLiftTest(1) {
        try unlift(one)
        catch {
          case e: Throwable => unlift(two)
        }
      }
    "failure" in
      runLiftTest(1) {
        try fail[Int]
        catch {
          case e: Exception => unlift(one)
        }
      }
  }

  "ensure" - {
    "success" in
      runLiftTest(1) {
        var i = 0
        def c() = i += 1
        try unlift(one)
        finally {
          c()
        }
        i
      }
    "failure" in
      runLiftTest(1) {
        var i = 0
        def c() = i += 1
        try {
          try unlift(one) / fail[Int]
          finally {
            c()
          }
        } catch {
          case e: Exception => 1
        }
        i
      }
  }
} 
Example 10
Source File: Cp.scala    From benchmarks   with Apache License 2.0 5 votes vote down vote up
package com.rossabaker
package benchmarks

import org.openjdk.jmh.annotations._

@State(Scope.Thread)
@Fork(2)
@Measurement(iterations = 10)
@Warmup(iterations = 10)
@Threads(1)
class Cp extends BenchmarkUtils {
  @Benchmark
  def fs2Sync(): Unit = {
    import _root_.fs2._, Stream._
    import java.nio.file.Paths
    io.file.readAll[Task](Paths.get("testdata/lorem-ipsum.txt"), 4096)
      .to(io.file.writeAll[Task](Paths.get("out/lorem-ipsum.txt")))
      .run
      .unsafeRun
  }

  @Benchmark
  def fs2Async(): Unit = {
    import _root_.fs2._, Stream._
    import java.nio.file.Paths
    io.file.readAllAsync[Task](Paths.get("testdata/lorem-ipsum.txt"), 4096)
      .to(io.file.writeAllAsync[Task](Paths.get("out/lorem-ipsum.txt")))
      .run
      .unsafeRun
  }

  @Benchmark
  def scalazStreamIo(): Unit = {
    import _root_.scalaz.stream._, Process._
    constant(4096)
      .through(io.fileChunkR("testdata/lorem-ipsum.txt"))
      .to(io.fileChunkW("out/lorem-ipsum.txt"))
      .run
      .unsafePerformSync
  }

  @Benchmark
  def scalazStreamNio(): Unit = {
    import _root_.scalaz.stream._, Process._
    constant(4096)
      .through(nio.file.chunkR("testdata/lorem-ipsum.txt"))
      .to(nio.file.chunkW("out/lorem-ipsum.txt"))
      .run
      .unsafePerformSync
  }

   }
            callback.onError(ex)
          }

          def onComplete(): Unit = {
            try {
              out.close()
              callback.onSuccess(())
            } catch {
              case NonFatal(ex) =>
                callback.onError(ex)
            }
          }
        }
      }

    Await.result(
      copyFile(new File("testdata/lorem-ipsum.txt"), new File("out/lorem-ipsum.txt"), 4096)
        .runAsync(monixScheduler),
      Duration.Inf
    )
  }
} 
Example 11
Source File: Monix.scala    From arrows   with Apache License 2.0 5 votes vote down vote up
package benchmarks

import monix.eval.Task
import monix.execution.Cancelable
import org.openjdk.jmh.annotations.Benchmark
import scala.util.Try
import scala.util.Success

trait MonixAsync {
  this: Benchmarks =>

  private[this] final val gen = MonixGen(dist)

  @Benchmark
  def monixTask = {
    import scala.concurrent._
    import scala.concurrent.duration._
    import monix.execution.Scheduler.Implicits.global
    Try(Await.result(gen(1).runAsync, Duration.Inf))
  }

}

trait MonixSync {
  this: Benchmarks =>

  private[this] final val gen = MonixGen(dist)

  @Benchmark
  def monixTask = {
    import monix.execution.Scheduler.Implicits.global
    Success(gen(1).runSyncMaybe.right.get)
  }
}

object MonixGen extends Gen[Int => Task[Int]] {

  def sync = Task.now _

  def async(schedule: Runnable => Unit) = {
    v =>
      Task.async[Int] {
        case (s, cb) =>
          schedule(() => cb.onSuccess(v))
          Cancelable.empty
      }
  }

  def failure(ex: Throwable) = _ => Task.raiseError(ex)

  def map(t: Int => Task[Int], f: Int => Int) =
    t.andThen(_.map(f))

  def flatMap(t: Int => Task[Int], f: Int => Task[Int]) =
    t.andThen(_.flatMap(f))

  def handle(t: Int => Task[Int], i: Int) =
    t.andThen(_.onErrorHandle(_ => i))
} 
Example 12
Source File: MonixRestImplicits.scala    From udash-core   with Apache License 2.0 5 votes vote down vote up
package io.udash
package rest.monix

import com.avsystem.commons._
import com.avsystem.commons.meta.MacroInstances
import com.avsystem.commons.misc.ImplicitNotFound
import io.udash.rest.raw.HttpResponseType
import io.udash.rest.raw.RawRest.{Async, AsyncEffect}
import io.udash.rest.{GenCodecRestImplicits, OpenApiFullInstances, RestOpenApiCompanion}
import monix.eval.Task
import monix.execution.Scheduler

import scala.annotation.implicitNotFound

trait MonixRestImplicits extends GenCodecRestImplicits {
  implicit def scheduler: Scheduler = Scheduler.global

  implicit def taskToAsync: AsyncEffect[Task] =
    new AsyncEffect[Task] {
      def toAsync[A](task: Task[A]): Async[A] =
        callback => task.runAsync(res => callback(res.fold(Failure(_), Success(_))))
      def fromAsync[A](async: Async[A]): Task[A] =
        Task.async(callback => async(res => callback(res.fold(Left(_), Right(_)))))
    }

  @implicitNotFound("${T} is not a valid HTTP method result type - it must be wrapped into a Task")
  implicit def httpResponseTypeNotFound[T]: ImplicitNotFound[HttpResponseType[T]] =
    ImplicitNotFound()
}
object MonixRestImplicits extends MonixRestImplicits

abstract class MonixRestApiCompanion[Real](
  implicit macroInstances: MacroInstances[MonixRestImplicits, OpenApiFullInstances[Real]]
) extends RestOpenApiCompanion[MonixRestImplicits, Real](MonixRestImplicits) 
Example 13
Source File: ArrayFillBenchmark.scala    From zio   with Apache License 2.0 5 votes vote down vote up
package zio

import java.util.concurrent.TimeUnit

import scala.collection.immutable.Range

import org.openjdk.jmh.annotations._

@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
class ArrayFillBenchmark {
  @Param(Array("10000"))
  var size: Int = _

  def createTestArray: Array[Int] = Range.inclusive(1, size).toArray.reverse

  @Benchmark
  def zioArrayFill() = {
    import IOBenchmarks.unsafeRun

    def arrayFill(array: Array[Int])(i: Int): UIO[Unit] =
      if (i >= array.length) UIO.unit
      else UIO(array.update(i, i)).flatMap(_ => arrayFill(array)(i + 1))

    unsafeRun(
      for {
        array <- IO.effectTotal[Array[Int]](createTestArray)
        _     <- arrayFill(array)(0)
      } yield ()
    )
  }

  @Benchmark
  def monoArrayFill() = {
    import reactor.core.publisher.Mono

    def arrayFill(array: Array[Int])(i: Int): Mono[Unit] =
      if (i >= array.length) Mono.fromSupplier(() => ())
      else
        Mono
          .fromSupplier(() => array.update(i, i))
          .flatMap(_ => arrayFill(array)(i + 1))

    (for {
      array <- Mono.fromSupplier(() => createTestArray)
      _     <- arrayFill(array)(0)
    } yield ())
      .block()
  }

  @Benchmark
  def catsArrayFill() = {
    import cats.effect.IO

    def arrayFill(array: Array[Int])(i: Int): IO[Unit] =
      if (i >= array.length) IO.unit
      else IO(array.update(i, i)).flatMap(_ => arrayFill(array)(i + 1))

    (for {
      array <- IO(createTestArray)
      _     <- arrayFill(array)(0)
    } yield ()).unsafeRunSync()
  }

  @Benchmark
  def monixArrayFill() = {
    import IOBenchmarks.monixScheduler
    import monix.eval.Task

    def arrayFill(array: Array[Int])(i: Int): Task[Unit] =
      if (i >= array.length) Task.unit
      else Task.eval(array.update(i, i)).flatMap(_ => arrayFill(array)(i + 1))

    (for {
      array <- Task.eval(createTestArray)
      _     <- arrayFill(array)(0)
    } yield ()).runSyncUnsafe(scala.concurrent.duration.Duration.Inf)
  }
} 
Example 14
Source File: ZIOArray.scala    From zio   with Apache License 2.0 5 votes vote down vote up
package zio

import scala.{ Array, Boolean, Int, Unit }

object ZIOArray {

  def bubbleSort[A](lessThanEqual0: (A, A) => Boolean)(array: Array[A]): UIO[Unit] = {
    def outerLoop(i: Int): UIO[Unit] =
      if (i >= array.length - 1) UIO.unit else innerLoop(i, i + 1).flatMap(_ => outerLoop(i + 1))

    def innerLoop(i: Int, j: Int): UIO[Unit] =
      if (j >= array.length) UIO.unit
      else
        UIO((array(i), array(j))).flatMap {
          case (ia, ja) =>
            val maybeSwap = if (lessThanEqual0(ia, ja)) UIO.unit else swapIJ(i, ia, j, ja)

            maybeSwap.flatMap(_ => innerLoop(i, j + 1))
        }

    def swapIJ(i: Int, ia: A, j: Int, ja: A): UIO[Unit] =
      UIO { array.update(i, ja); array.update(j, ia) }

    outerLoop(0)
  }
}

object CatsIOArray {
  import cats.effect.IO

  def bubbleSort[A](lessThanEqual0: (A, A) => Boolean)(array: Array[A]): IO[Unit] = {
    def outerLoop(i: Int): IO[Unit] =
      if (i >= array.length - 1) IO.unit else innerLoop(i, i + 1).flatMap(_ => outerLoop(i + 1))

    def innerLoop(i: Int, j: Int): IO[Unit] =
      if (j >= array.length) IO.unit
      else
        IO((array(i), array(j))).flatMap {
          case (ia, ja) =>
            val maybeSwap = if (lessThanEqual0(ia, ja)) IO.unit else swapIJ(i, ia, j, ja)

            maybeSwap.flatMap(_ => innerLoop(i, j + 1))
        }

    def swapIJ(i: Int, ia: A, j: Int, ja: A): IO[Unit] =
      IO { array.update(i, ja); array.update(j, ia) }

    outerLoop(0)
  }
}

object MonixIOArray {
  import monix.eval.Task

  def bubbleSort[A](lessThanEqual0: (A, A) => Boolean)(array: Array[A]): Task[Unit] = {
    def outerLoop(i: Int): Task[Unit] =
      if (i >= array.length - 1) Task.unit else innerLoop(i, i + 1).flatMap(_ => outerLoop(i + 1))

    def innerLoop(i: Int, j: Int): Task[Unit] =
      if (j >= array.length) Task.unit
      else
        Task.eval((array(i), array(j))).flatMap {
          case (ia, ja) =>
            val maybeSwap = if (lessThanEqual0(ia, ja)) Task.unit else swapIJ(i, ia, j, ja)

            maybeSwap.flatMap(_ => innerLoop(i, j + 1))
        }

    def swapIJ(i: Int, ia: A, j: Int, ja: A): Task[Unit] =
      Task.eval { array.update(i, ja); array.update(j, ia) }

    outerLoop(0)
  }
} 
Example 15
Source File: IOEmptyRaceBenchmark.scala    From zio   with Apache License 2.0 5 votes vote down vote up
package zio

import java.util.concurrent.TimeUnit

import org.openjdk.jmh.annotations._

import zio.IOBenchmarks._

@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
@Warmup(iterations = 10, time = 3, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 10, time = 3, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@Threads(1)
class IOEmptyRaceBenchmark {
  @Param(Array("1000"))
  var size: Int = _

  @Benchmark
  def monixEmptyRace(): Int = {
    import monix.eval.Task

    def loop(i: Int): monix.eval.Task[Int] =
      if (i < size) Task.race(Task.never, Task.eval(i + 1)).flatMap(_ => loop(i + 1))
      else Task.pure(i)

    loop(0).runSyncUnsafe()
  }

  @Benchmark
  def catsEmptyRace(): Int = {
    import cats.effect.IO

    def loop(i: Int): IO[Int] =
      if (i < size) IO.race(IO.never, IO.delay(i + 1)).flatMap(_ => loop(i + 1))
      else IO.pure(i)

    loop(0).unsafeRunSync()
  }

  @Benchmark
  def zioEmptyRace(): Int = zioEmptyRace(IOBenchmarks)

  @Benchmark
  def zioTracedEmptyRace(): Int = zioEmptyRace(TracedRuntime)

  private[this] def zioEmptyRace(runtime: Runtime[Any]): Int = {
    def loop(i: Int): UIO[Int] =
      if (i < size) IO.never.raceFirst(IO.effectTotal(i + 1)).flatMap(loop)
      else IO.succeedNow(i)

    runtime.unsafeRun(loop(0))
  }
} 
Example 16
Source File: BubbleSortBenchmarks.scala    From zio   with Apache License 2.0 5 votes vote down vote up
package zio

import java.util.concurrent.TimeUnit

import scala.collection.immutable.Range

import IOBenchmarks.unsafeRun
import org.openjdk.jmh.annotations._

@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
class BubbleSortBenchmarks {
  @Param(Array("1000"))
  var size: Int = _

  def createTestArray: Array[Int] = Range.inclusive(1, size).toArray.reverse
  def assertSorted(array: Array[Int]): Unit =
    if (!array.sorted.sameElements(array)) {
      throw new Exception("Array not correctly sorted")
    }

  @Benchmark
  def zioBubbleSort() = {
    import ZIOArray._

    unsafeRun(
      for {
        array <- IO.effectTotal[Array[Int]](createTestArray)
        _     <- bubbleSort[Int](_ <= _)(array)
        _     <- IO.effectTotal[Unit](assertSorted(array))
      } yield ()
    )
  }
  @Benchmark
  def catsBubbleSort() = {
    import CatsIOArray._
    import cats.effect.IO

    (for {
      array <- IO(createTestArray)
      _     <- bubbleSort[Int](_ <= _)(array)
      _     <- IO(assertSorted(array))
    } yield ()).unsafeRunSync()
  }
  @Benchmark
  def monixBubbleSort() = {
    import IOBenchmarks.monixScheduler
    import MonixIOArray._
    import monix.eval.Task

    (for {
      array <- Task.eval(createTestArray)
      _     <- bubbleSort[Int](_ <= _)(array)
      _     <- Task.eval(assertSorted(array))
    } yield ()).runSyncUnsafe(scala.concurrent.duration.Duration.Inf)
  }
} 
Example 17
Source File: IODeepLeftBindBenchmark.scala    From zio   with Apache License 2.0 5 votes vote down vote up
package zio

import java.util.concurrent.TimeUnit

import org.openjdk.jmh.annotations._

import zio.IOBenchmarks._

@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
class IODeepLeftBindBenchmark {
  @Param(Array("10000"))
  var depth: Int = _

  @Benchmark
  def monixDeepLeftBindBenchmark(): Int = {
    import monix.eval.Task

    var i  = 0
    var io = Task.eval(i)
    while (i < depth) {
      io = io.flatMap(i => Task.eval(i))
      i += 1
    }

    io.runSyncStep.right.get
  }

  @Benchmark
  def zioDeepLeftBindBenchmark(): Int = zioDeepLeftBindBenchmark(IOBenchmarks)

  @Benchmark
  def zioTracedDeepLeftBindBenchmark(): Int = zioDeepLeftBindBenchmark(TracedRuntime)

  def zioDeepLeftBindBenchmark(runtime: Runtime[Any]): Int = {
    var i  = 0
    var io = IO.effectTotal(i)
    while (i < depth) {
      io = io.flatMap(i => IO.effectTotal(i))
      i += 1
    }

    runtime.unsafeRun(io)
  }

  @Benchmark
  def catsDeepLeftBindBenchmark(): Int = {
    import cats.effect.IO

    var i  = 0
    var io = IO(i)
    while (i < depth) {
      io = io.flatMap(i => IO(i))
      i += 1
    }

    io.unsafeRunSync
  }

} 
Example 18
Source File: IOForkInterruptBenchmark.scala    From zio   with Apache License 2.0 5 votes vote down vote up
package zio

import java.util.concurrent.TimeUnit

import cats.effect.concurrent.Deferred
import org.openjdk.jmh.annotations._

import zio.IOBenchmarks._

@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
@Warmup(iterations = 10, time = 3, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 10, time = 3, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@Threads(1)
class IOForkInterruptBenchmark {
  @Param(Array("100"))
  var size: Int = _

  @Benchmark
  def monixForkInterrupt(): Unit = {
    import monix.eval.Task

    def loop(i: Int): monix.eval.Task[Unit] =
      if (i < size) Deferred[Task, Unit].flatMap { p1 =>
        Deferred[Task, Unit].flatMap { p2 =>
          p1.complete(())
            .flatMap(_ => Task.never)
            .guarantee(p2.complete(()))
            .start
            .flatMap(f => p1.get.flatMap(_ => f.cancel.flatMap(_ => p2.get.flatMap(_ => loop(i + 1)))))
        }
      }
      else Task.unit

    loop(0).runSyncUnsafe()
  }

  @Benchmark
  def catsForkInterrupt(): Unit = {
    import cats.effect.IO

    def loop(i: Int): IO[Unit] =
      if (i < size)
        Deferred[IO, Unit].flatMap { p1 =>
          Deferred[IO, Unit].flatMap { p2 =>
            p1.complete(())
              .flatMap(_ => IO.never)
              .guarantee(p2.complete(()))
              .start
              .flatMap(f => p1.get.flatMap(_ => f.cancel.flatMap(_ => p2.get.flatMap(_ => loop(i + 1)))))
          }
        }
      else IO.unit

    loop(0).unsafeRunSync()
  }

  @Benchmark
  def zioForkInterrupt(): Unit = zioForkInterrupt(IOBenchmarks)

  @Benchmark
  def zioTracedForkInterrupt(): Unit = zioForkInterrupt(TracedRuntime)

  private[this] def zioForkInterrupt(runtime: Runtime[Any]): Unit = {
    def loop(i: Int): UIO[Unit] =
      if (i < size) IO.never.fork.flatMap(_.interrupt *> loop(i + 1))
      else IO.unit

    runtime.unsafeRun(loop(0))
  }
} 
Example 19
Source File: Endpoint.scala    From lsp4s   with Apache License 2.0 5 votes vote down vote up
package scala.meta.jsonrpc

import io.circe.Decoder
import io.circe.Encoder
import monix.eval.Task
import monix.execution.Ack
import scala.concurrent.Future

class Endpoint[A: Decoder: Encoder, B: Decoder: Encoder](val method: String) {
  def encoderA: Encoder[A] = implicitly
  def decoderA: Decoder[A] = implicitly
  def encoderB: Encoder[B] = implicitly
  def decoderB: Decoder[B] = implicitly

  def request(request: A)(
      implicit client: JsonRpcClient
  ): Task[Either[Response.Error, B]] =
    client.request[A, B](method, request)
  def notify(
      notification: A
  )(implicit client: JsonRpcClient): Future[Ack] =
    client.notify[A](method, notification)
}

object Endpoint {
  def request[A: Decoder: Encoder, B: Decoder: Encoder](
      method: String
  ): Endpoint[A, B] =
    new Endpoint(method)
  def notification[A: Decoder: Encoder](method: String): Endpoint[A, Unit] =
    new Endpoint(method)
} 
Example 20
Source File: JsonRpcClient.scala    From lsp4s   with Apache License 2.0 5 votes vote down vote up
package scala.meta.jsonrpc

import scala.concurrent.Future
import io.circe.Decoder
import io.circe.Encoder
import monix.eval.Task
import monix.execution.Ack

trait JsonRpcClient {
  final def notify[A](
      endpoint: Endpoint[A, Unit],
      notification: A
  ): Future[Ack] = notify[A](endpoint.method, notification)(endpoint.encoderA)
  def notify[A: Encoder](method: String, notification: A): Future[Ack]
  def serverRespond(response: Response): Future[Ack]
  def clientRespond(response: Response): Unit
  final def request[A, B](
      endpoint: Endpoint[A, B],
      req: A
  ): Task[Either[Response.Error, B]] =
    request[A, B](endpoint.method, req)(endpoint.encoderA, endpoint.decoderB)
  def request[A: Encoder, B: Decoder](
      method: String,
      request: A
  ): Task[Either[Response.Error, B]]
}

object JsonRpcClient {
  val empty: JsonRpcClient = new JsonRpcClient {
    override def notify[A: Encoder](
        method: String,
        notification: A
    ): Future[Ack] = Ack.Continue
    override def serverRespond(response: Response): Future[Ack] = Ack.Continue
    override def clientRespond(response: Response): Unit = ()
    override def request[A: Encoder, B: Decoder](
        method: String,
        request: A
    ): Task[Either[Response.Error, B]] = Task.never
  }
} 
Example 21
Source File: LanguageClient.scala    From lsp4s   with Apache License 2.0 5 votes vote down vote up
package scala.meta.jsonrpc

import cats.syntax.either._
import io.circe.Decoder
import io.circe.Encoder
import io.circe.syntax._
import java.io.OutputStream
import java.nio.ByteBuffer
import monix.eval.Callback
import monix.eval.Task
import monix.execution.Ack
import monix.execution.Cancelable
import monix.execution.atomic.Atomic
import monix.execution.atomic.AtomicInt
import monix.reactive.Observer
import scala.collection.concurrent.TrieMap
import scala.concurrent.Future
import scala.concurrent.duration.Duration
import MonixEnrichments._
import scribe.LoggerSupport

class LanguageClient(out: Observer[ByteBuffer], logger: LoggerSupport)
    extends JsonRpcClient {
  def this(out: OutputStream, logger: LoggerSupport) =
    this(Observer.fromOutputStream(out, logger), logger)
  private val writer = new MessageWriter(out, logger)
  private val counter: AtomicInt = Atomic(1)
  private val activeServerRequests =
    TrieMap.empty[RequestId, Callback[Response]]
  def notify[A: Encoder](method: String, notification: A): Future[Ack] =
    writer.write(Notification(method, Some(notification.asJson)))
  def serverRespond(response: Response): Future[Ack] = response match {
    case Response.Empty => Ack.Continue
    case x: Response.Success => writer.write(x)
    case x: Response.Error =>
      logger.error(s"Response error: $x")
      writer.write(x)
  }
  def clientRespond(response: Response): Unit =
    for {
      id <- response match {
        case Response.Empty => None
        case Response.Success(_, requestId) => Some(requestId)
        case Response.Error(_, requestId) => Some(requestId)
      }
      callback <- activeServerRequests.get(id).orElse {
        logger.error(s"Response to unknown request: $response")
        None
      }
    } {
      activeServerRequests.remove(id)
      callback.onSuccess(response)
    }

  def request[A: Encoder, B: Decoder](
      method: String,
      request: A
  ): Task[Either[Response.Error, B]] = {
    val nextId = RequestId(counter.incrementAndGet())
    val response = Task.create[Response] { (out, cb) =>
      val scheduled = out.scheduleOnce(Duration(0, "s")) {
        val json = Request(method, Some(request.asJson), nextId)
        activeServerRequests.put(nextId, cb)
        writer.write(json)
      }
      Cancelable { () =>
        scheduled.cancel()
        this.notify("$/cancelRequest", CancelParams(nextId.value))
      }
    }
    response.map {
      case Response.Empty =>
        Left(
          Response.invalidParams(
            s"Got empty response for request $request",
            nextId
          )
        )
      case err: Response.Error =>
        Left(err)
      case Response.Success(result, _) =>
        result.as[B].leftMap { err =>
          Response.invalidParams(err.toString, nextId)
        }
    }
  }
}

object LanguageClient {
  def fromOutputStream(out: OutputStream, logger: LoggerSupport) =
    new LanguageClient(Observer.fromOutputStream(out, logger), logger)
} 
Example 22
Source File: Message.scala    From lsp4s   with Apache License 2.0 5 votes vote down vote up
package scala.meta.jsonrpc

import monix.eval.Task
import io.circe.Json
import io.circe.Decoder
import io.circe.Encoder
import io.circe.JsonObject
import io.circe.derivation.annotations.JsonCodec
import io.circe.syntax._

import cats.syntax.either._

sealed trait Message
object Message {
  implicit val encoder: Encoder[Message] = new Encoder[Message] {
    override def apply(a: Message): Json = {
      val json = a match {
        case r: Request => r.asJson
        case r: Notification => r.asJson
        case r: Response => r.asJson
      }
      json.mapObject(_.add("jsonrpc", "2.0".asJson))
    }
  }
  implicit val decoder: Decoder[Message] =
    Decoder.decodeJsonObject.emap { obj =>
      val json = Json.fromJsonObject(obj)
      val result =
        if (obj.contains("id"))
          if (obj.contains("error")) json.as[Response.Error]
          else if (obj.contains("result")) json.as[Response.Success]
          else json.as[Request]
        else json.as[Notification]
      result.leftMap(_.toString)
    }
}

@JsonCodec case class Request(
    method: String,
    params: Option[Json],
    id: RequestId
) extends Message {
  def toError(code: ErrorCode, message: String): Response =
    Response.error(ErrorObject(code, message, None), id)
}

@JsonCodec case class Notification(method: String, params: Option[Json])
    extends Message

sealed trait Response extends Message {
  def isSuccess: Boolean = this.isInstanceOf[Response.Success]
}
object Response {
  implicit val encoderResponse: Encoder[Response] = new Encoder[Response] {
    override def apply(a: Response): Json = a match {
      case r: Response.Success => r.asJson
      case r: Response.Error => r.asJson
      case Response.Empty => JsonObject.empty.asJson
    }
  }
  @JsonCodec case class Success(result: Json, id: RequestId) extends Response
  @JsonCodec case class Error(error: ErrorObject, id: RequestId)
      extends Response
  case object Empty extends Response
  def empty: Response = Empty
  def ok(result: Json, id: RequestId): Response =
    success(result, id)
  def okAsync[T](value: T): Task[Either[Response.Error, T]] =
    Task(Right(value))
  def success(result: Json, id: RequestId): Response =
    Success(result, id)
  def error(error: ErrorObject, id: RequestId): Response.Error =
    Error(error, id)
  def internalError(message: String): Response.Error =
    internalError(message, RequestId.Null)
  def internalError(message: String, id: RequestId): Response.Error =
    Error(ErrorObject(ErrorCode.InternalError, message, None), id)
  def invalidParams(message: String): Response.Error =
    invalidParams(message, RequestId.Null)
  def invalidParams(message: String, id: RequestId): Response.Error =
    Error(ErrorObject(ErrorCode.InvalidParams, message, None), id)
  def invalidRequest(message: String): Response.Error =
    Error(
      ErrorObject(ErrorCode.InvalidRequest, message, None),
      RequestId.Null
    )
  def cancelled(id: Json): Response.Error =
    Error(
      ErrorObject(ErrorCode.RequestCancelled, "", None),
      id.as[RequestId].getOrElse(RequestId.Null)
    )
  def parseError(message: String): Response.Error =
    Error(ErrorObject(ErrorCode.ParseError, message, None), RequestId.Null)
  def methodNotFound(message: String, id: RequestId): Response.Error =
    Error(ErrorObject(ErrorCode.MethodNotFound, message, None), id)
} 
Example 23
Source File: MonixParallelTests.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package freestyle.free.tests

import freestyle.free.implicits._
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.scalatest.{Matchers, WordSpec}
import scala.concurrent.Await
import scala.concurrent.duration.Duration

class MonixParallelTests extends WordSpec with Matchers {
  "Applicative Parallel Support" should {
    "allow non deterministic execution when interpreting to monix.eval.Task" ignore {

      val test = new freestyle.NonDeterminismTestShared
      import test._

      implicit val interpreter = new freestyle.MixedFreeS.Handler[Task] {
        override def x: Task[Int] = Task(blocker(1, 1000L))
        override def y: Task[Int] = Task(blocker(2, 0L))
        override def z: Task[Int] = Task(blocker(3, 2000L))
      }

      Await.result(program.interpret[Task].runAsync, Duration.Inf) shouldBe List(3, 1, 2, 3)
      buf.toArray shouldBe Array(3, 2, 1, 3)
    }
  }
} 
Example 24
Source File: WatchServiceObservable.scala    From monix-nio   with Apache License 2.0 5 votes vote down vote up
package monix.nio

import java.nio.file.WatchEvent

import monix.eval.Task
import monix.execution.Ack.{ Continue, Stop }
import monix.execution.atomic.Atomic
import monix.execution.cancelables.SingleAssignCancelable
import monix.execution.exceptions.APIContractViolationException
import monix.execution.{ Callback, Cancelable, Scheduler }
import monix.reactive.Observable
import monix.reactive.observers.Subscriber

import scala.concurrent.Future
import scala.util.control.NonFatal

abstract class WatchServiceObservable extends Observable[Array[WatchEvent[_]]] {
  def watchService: Option[WatchService]

  private[this] val wasSubscribed = Atomic(false)
  override def unsafeSubscribeFn(subscriber: Subscriber[Array[WatchEvent[_]]]): Cancelable = {
    if (wasSubscribed.getAndSet(true)) {
      subscriber.onError(APIContractViolationException(this.getClass.getName))
      Cancelable.empty
    } else try startPolling(subscriber) catch {
      case NonFatal(e) =>
        subscriber.onError(e)
        Cancelable.empty
    }
  }

  def init(subscriber: Subscriber[Array[WatchEvent[_]]]): Future[Unit] =
    Future.successful(())

  private def startPolling(subscriber: Subscriber[Array[WatchEvent[_]]]): Cancelable = {
    import subscriber.scheduler

    val taskCallback = new Callback[Throwable, Array[WatchEvent[_]]]() {
      override def onSuccess(value: Array[WatchEvent[_]]): Unit = {}
      override def onError(ex: Throwable): Unit = {
        subscriber.onError(ex)
      }
    }
    val cancelable = Task
      .fromFuture(init(subscriber))
      .flatMap { _ =>
        loop(subscriber)
      }
      .executeWithOptions(_.enableAutoCancelableRunLoops)
      .runAsync(taskCallback)

    val extraCancelable = Cancelable(() => {
      cancelable.cancel()
    })
    SingleAssignCancelable.plusOne(extraCancelable)
  }

  private def loop(subscriber: Subscriber[Array[WatchEvent[_]]])(implicit scheduler: Scheduler): Task[Array[WatchEvent[_]]] = {
    import collection.JavaConverters._
    watchService.map { ws =>
      ws.take()
        .doOnCancel(Task.defer(ws.close()))
        .flatMap { key =>
          val events = key.pollEvents().asScala.toArray
          key.reset()
          Task.fromFuture(subscriber.onNext(events)).flatMap {
            case Continue => loop(subscriber)
            case Stop => emptyTask
          }
        }
    }
  }.getOrElse(emptyTask)

  private val emptyTask = Task.create[Array[WatchEvent[_]]]((_, _) => Cancelable.empty)
} 
Example 25
Source File: AsyncSocketChannelClient.scala    From monix-nio   with Apache License 2.0 5 votes vote down vote up
package monix.nio.tcp

import monix.eval.Task
import monix.execution.Scheduler


  def close(): Task[Unit] =
    taskSocketChannel.fold(Task.pure(()))(_.close())
}

object AsyncSocketChannelClient {

  def apply(
    host: String,
    port: Int,
    bufferSize: Int)(implicit scheduler: Scheduler): AsyncSocketChannelClient = {

    val client = new AsyncSocketChannelClient(host, port, bufferSize)
    client.init()
    client
  }

  def apply(
    taskSocketChannel: TaskSocketChannel,
    bufferSize: Int)(implicit scheduler: Scheduler): AsyncSocketChannelClient = {

    val client = new AsyncSocketChannelClient(taskSocketChannel, bufferSize)
    client.init()
    client
  }
} 
Example 26
Source File: AsyncChannelObservable.scala    From monix-nio   with Apache License 2.0 5 votes vote down vote up
package monix.nio

import java.nio.ByteBuffer

import monix.eval.Task
import monix.execution.Ack.{ Continue, Stop }
import monix.execution.{ Callback, Cancelable, Scheduler }
import monix.execution.atomic.Atomic
import monix.execution.cancelables.SingleAssignCancelable
import monix.execution.exceptions.APIContractViolationException
import monix.nio.internal.{ Bytes, EmptyBytes, NonEmptyBytes }
import monix.reactive.Observable
import monix.reactive.observers.Subscriber

import scala.concurrent.Future
import scala.util.control.NonFatal

private[nio] abstract class AsyncChannelObservable extends Observable[Array[Byte]] {
  def bufferSize: Int
  def channel: Option[AsyncChannel]
  def init(subscriber: Subscriber[Array[Byte]]): Future[Unit] =
    Future.successful(())

  private[this] val wasSubscribed = Atomic(false)
  override def unsafeSubscribeFn(subscriber: Subscriber[Array[Byte]]): Cancelable = {
    import subscriber.scheduler
    if (wasSubscribed.getAndSet(true)) {
      subscriber.onError(APIContractViolationException(this.getClass.getName))
      Cancelable.empty
    } else try startReading(subscriber) catch {
      case NonFatal(e) =>
        subscriber.onError(e)
        closeChannel()
        Cancelable.empty
    }
  }

  private def startReading(subscriber: Subscriber[Array[Byte]]): Cancelable = {
    import subscriber.scheduler

    val taskCallback = new Callback[Throwable, Array[Byte]]() {
      override def onSuccess(value: Array[Byte]): Unit = {
        channel.collect { case sc if sc.closeOnComplete => closeChannel() }
      }
      override def onError(ex: Throwable): Unit = {
        closeChannel()
        subscriber.onError(ex)
      }
    }
    val cancelable = Task
      .fromFuture(init(subscriber))
      .flatMap { _ =>
        loop(subscriber, 0)
      }
      .executeWithOptions(_.enableAutoCancelableRunLoops)
      .runAsync(taskCallback)

    val extraCancelable = Cancelable(() => {
      cancelable.cancel()
      closeChannel()
    })
    SingleAssignCancelable.plusOne(extraCancelable)
  }

  private[this] val buffer = ByteBuffer.allocate(bufferSize)
  private def loop(subscriber: Subscriber[Array[Byte]], position: Long)(implicit scheduler: Scheduler): Task[Array[Byte]] = {
    buffer.clear()
    channel.map { ch =>
      ch
        .read(buffer, position)
        .doOnCancel(Task.defer(ch.close()))
        .flatMap { result =>
          val bytes = Bytes(buffer, result)
          bytes match {
            case EmptyBytes =>
              subscriber.onComplete()
              Task.now(Bytes.emptyBytes)

            case NonEmptyBytes(arr) =>
              Task.fromFuture(subscriber.onNext(arr)).flatMap {
                case Continue =>
                  loop(subscriber, position + result)

                case Stop =>
                  Task.now(Bytes.emptyBytes)
              }
          }
        }
    }.getOrElse(Task.now(Bytes.emptyBytes))
  }

  private[nio] final def closeChannel()(implicit scheduler: Scheduler) =
    channel.foreach(_.close().runToFuture)
} 
Example 27
Source File: AsyncWatchServiceObservable.scala    From monix-nio   with Apache License 2.0 5 votes vote down vote up
package monix.nio.file

import java.nio.file.WatchKey

import monix.eval.Task
import monix.nio.WatchServiceObservable

import scala.concurrent.duration.TimeUnit

final class AsyncWatchServiceObservable(taskWatchService: TaskWatchService) extends WatchServiceObservable {
  override def watchService = Option {
    asyncWatchServiceWrapper(taskWatchService)
  }

  private[file] def asyncWatchServiceWrapper(taskWatchService: TaskWatchService) = new monix.nio.WatchService {
    override def poll(timeout: Long, timeUnit: TimeUnit): Task[Option[WatchKey]] = taskWatchService.poll(timeout, timeUnit)
    override def poll(): Task[Option[WatchKey]] = taskWatchService.poll()
    override def take(): Task[WatchKey] = taskWatchService.take()
    override def close(): Task[Unit] = taskWatchService.close()
  }
} 
Example 28
Source File: TaskWatchService.scala    From monix-nio   with Apache License 2.0 5 votes vote down vote up
package monix.nio.file

import java.nio.file.WatchEvent.Kind
import java.nio.file.{ Path, WatchKey }

import monix.eval.Task
import monix.execution.{ Callback, Scheduler }

import scala.concurrent.duration.TimeUnit


abstract class TaskWatchService {

  protected val watchService: WatchService

  def poll(timeout: Long, timeUnit: TimeUnit): Task[Option[WatchKey]] =
    Task.create { (scheduler, cb) =>
      watchService.poll(timeout, timeUnit, Callback.forked(cb)(scheduler))
    }

  def poll(): Task[Option[WatchKey]] =
    Task.create { (scheduler, cb) =>
      watchService.poll(Callback.forked(cb)(scheduler))
    }

  def take(): Task[WatchKey] =
    Task.create { (scheduler, cb) =>
      watchService.take(Callback.forked(cb)(scheduler))
    }

  def close(): Task[Unit] =
    Task.now(watchService.close())
}

object TaskWatchService {
  def apply(path: Path, events: Kind[_]*)(implicit s: Scheduler): TaskWatchService = {
    new TaskWatchService {
      override val watchService: WatchService = WatchService.apply(path, events: _*)
    }
  }
} 
Example 29
Source File: UdpIntegrationSpec.scala    From monix-nio   with Apache License 2.0 5 votes vote down vote up
package monix.nio.udp

import java.net.InetSocketAddress

import minitest.SimpleTestSuite
import monix.eval.Task
import monix.execution.Ack
import monix.execution.Ack.{ Continue, Stop }
import monix.reactive.Observable

import scala.concurrent.duration._
import scala.concurrent.{ Await, Promise }

object UdpIntegrationSpec extends SimpleTestSuite {
  implicit val ctx = monix.execution.Scheduler.Implicits.global

  test("send and receive UDP packets successfully") {
    val data = Array.fill(8)("monix")

    val writes = (ch: TaskDatagramChannel, to: InetSocketAddress) => Observable
      .fromIterable(data)
      .mapEval(data => ch.send(Packet(data.getBytes, to)))

    val readsPromise = Promise[String]()
    val recv = new StringBuilder("")
    val reads = (ch: TaskDatagramChannel, maxSize: Int) => Observable
      .repeatEval(ch.receive(maxSize, 2.seconds))
      .mapEval(t => t)
      .map { packet =>
        packet.foreach(p => recv.append(new String(p.data)))
        packet
      }
      .guaranteeCase(_ => Task(readsPromise.success(recv.mkString)))
      .subscribe(_.fold[Ack](Stop)(_ => Continue))

    val program = for {
      ch <- bind("localhost", 2115).map { ch =>
        reads(ch, 64)
        ch
      }
      sent <- writes(ch, new InetSocketAddress("localhost", 2115)).sumL
      received <- Task.fromFuture(readsPromise.future)
      _ <- ch.close()
    } yield sent == 40 & received == data.mkString("")

    val result = Await.result(program.runToFuture, 10.seconds)
    assert(result)
  }
} 
Example 30
Source File: WatchServiceTest.scala    From monix-nio   with Apache License 2.0 5 votes vote down vote up
package monix.nio.file

import java.io.File
import java.nio.file.{ Paths, WatchEvent }

import minitest.SimpleTestSuite
import monix.eval.Task
import monix.execution.Ack.{ Continue, Stop }

import scala.concurrent.duration._
import scala.concurrent.{ Await, Promise }
object WatchServiceTest extends SimpleTestSuite {
  implicit val ctx = monix.execution.Scheduler.Implicits.global

  test("file event captured") {
    val path = Paths.get(System.getProperty("java.io.tmpdir"))

    val watchP = Promise[Boolean]()
    val watchT = Task.evalAsync {
      watchAsync(path).timeoutOnSlowUpstream(10.seconds).subscribe(
        (events: Array[WatchEvent[_]]) => {
          val captured = events.find(e => s"${e.kind().name()} - ${e.context().toString}".contains("monix"))
          if (captured.isDefined) {
            watchP.success(true)
            Stop
          } else {
            Continue
          }
        },
        err => watchP.failure(err),
        () => watchP.success(true))
    }
    val fileT = Task.evalAsync {
      val temp = File.createTempFile("monix", ".tmp", path.toFile)
      Thread.sleep(2000)
      temp.delete()
    }

    watchT.runToFuture
    fileT.runToFuture

    val result = Await.result(watchP.future, 20.seconds)
    assert(result)
  }

} 
Example 31
Source File: FileChannelForTesting.scala    From monix-nio   with Apache License 2.0 5 votes vote down vote up
package monix.nio.file

import java.nio.ByteBuffer

import monix.eval.Task
import monix.execution.{ Callback, Scheduler }
import monix.execution.atomic.Atomic
import monix.nio.AsyncChannel

class FileChannelForTesting(
  readingSeq: Vector[Array[Byte]],
  writeSeq: Atomic[Vector[Array[Byte]]])(implicit s: Scheduler) extends TaskFileChannel with AsyncChannel {

  private val asyncFileChannelForTesting =
    new AsyncFileChannelForTesting(readingSeq, writeSeq)

  override val closeOnComplete: Boolean = true
  override val asyncFileChannel: AsyncFileChannel = asyncFileChannelForTesting

  def isClosed = asyncFileChannelForTesting.isClosed
  def getBytesReadPosition = asyncFileChannelForTesting.getBytesReadPosition
  def getBytesWritePosition = asyncFileChannelForTesting.getBytesWritePosition
  def createReadException() = asyncFileChannelForTesting.createReadException()
  def createWriteException() = asyncFileChannelForTesting.createWriteException()

  private final class AsyncFileChannelForTesting(
    readingSeq: Vector[Array[Byte]],
    writeSeq: Atomic[Vector[Array[Byte]]])(implicit s: Scheduler) extends AsyncFileChannel {

    private val readChannelPosition = Atomic(0)
    private val writeChannelPosition = Atomic(0)
    private val channelClosed = Atomic(false)
    private val readException = Atomic(false)
    private val writeException = Atomic(false)

    def isClosed = channelClosed.get
    def getBytesReadPosition = readChannelPosition.get
    def getBytesWritePosition = writeChannelPosition.get
    def createReadException() = readException.set(true)
    def createWriteException() = writeException.set(true)

    def taskCallback(handler: Callback[Throwable, Int]) = new Callback[Throwable, Array[Byte]]() {
      override def onSuccess(value: Array[Byte]): Unit = handler.onSuccess(value.length)
      override def onError(ex: Throwable): Unit = handler.onError(ex)
    }

    override def isOpen: Boolean = !isClosed
    override def flush(writeMetaData: Boolean, cb: Callback[Throwable, Unit]): Unit = ???
    override def size(cb: Callback[Throwable, Long]): Unit = () //not really used
    override def read(dst: ByteBuffer, position: Long, handler: Callback[Throwable, Int]) = {
      if (readException.get) handler.onError(new Exception("Test Exception"))
      else if (readChannelPosition.get < readingSeq.size) {
        val pos = readChannelPosition.getAndIncrement()

        val r = Task {
          val elem = readingSeq(pos)
          dst.put(elem)
          elem
        }
        r.runAsync(taskCallback(handler))
      } else {
        handler.onSuccess(-1)
      }

    }
    override def write(b: ByteBuffer, position: Long, handler: Callback[Throwable, Int]) = {
      if (writeException.get) handler.onError(new Exception("Test Exception"))
      else {
        val pos = writeChannelPosition.getAndIncrement()
        val r = Task {
          val bytes = b.array()
          writeSeq.transform { v =>
            if (v.size > pos) v.updated(pos, bytes)
            else v :+ bytes
          }
          bytes
        }
        r.runAsync(taskCallback(handler))
      }
    }
    override def close() = {
      channelClosed.set(true)
    }
  }
} 
Example 32
Source File: CodecTest.scala    From monix-nio   with Apache License 2.0 5 votes vote down vote up
package monix.nio.file

import java.nio.file.{ Files, Paths }
import java.util

import minitest.SimpleTestSuite
import monix.eval.Task
import monix.execution.Callback
import monix.execution.Scheduler.Implicits.{ global => ctx }
import monix.nio.file
import monix.nio.text.UTF8Codec.{ utf8Decode, utf8Encode }
import monix.reactive.Observable

import scala.concurrent.duration._
import scala.concurrent.{ Await, Promise }

object CodecTest extends SimpleTestSuite {
  test("decode file utf8") {
    val from = Paths.get(this.getClass.getResource("/testFiles/specialChars.txt").toURI)

    val p = Promise[Seq[Byte]]()
    val callback = new Callback[Throwable, List[Array[Byte]]] {
      override def onSuccess(value: List[Array[Byte]]): Unit = p.success(value.flatten)
      override def onError(ex: Throwable): Unit = p.failure(ex)
    }

    readAsync(from, 3)
      .pipeThrough(utf8Decode)
      .pipeThrough(utf8Encode)
      .toListL
      .runAsync(callback)
    val result = Await.result(p.future, 3.second)
    val f1 = Files.readAllBytes(from)
    val f2 = result
    assert(util.Arrays.equals(f1, f2.toArray))
  }

  test("decode special chars") {
    val strSeq = Seq("A", "\u0024", "\u00A2", "\u20AC", new String(Array(0xF0, 0x90, 0x8D, 0x88).map(_.toByte)), "B")

    for (grouping <- 1 to 12) {
      val obsSeq =
        Observable
          .fromIterator(Task(strSeq.flatMap(_.getBytes).grouped(grouping).map(_.toArray)))
          .pipeThrough(utf8Decode)

      val p = Promise[Boolean]()
      val callback = new Callback[Throwable, List[String]] {
        override def onSuccess(value: List[String]): Unit = {
          p.success(if (value.mkString == strSeq.mkString) true else false)
        }

        override def onError(ex: Throwable): Unit = p.failure(ex)
      }
      obsSeq.toListL.runAsync(callback)
      val result = Await.result(p.future, 3.second)
      assert(result)
    }
  }

  test("copy file utf8") {
    val from = Paths.get(this.getClass.getResource("/testFiles/specialChars.txt").toURI)
    val to = Paths.get("src/test/resources/res.txt")
    val consumer = file.writeAsync(to)
    val p = Promise[Long]()
    val callback = new Callback[Throwable, Long] {
      override def onSuccess(value: Long): Unit = p.success(value)
      override def onError(ex: Throwable): Unit = p.failure(ex)
    }

    readAsync(from, 3)
      .pipeThrough(utf8Decode)
      .map { str =>
        //Console.println(str)
        str
      }
      .pipeThrough(utf8Encode)
      .consumeWith(consumer)
      .runAsync(callback)
    val result = Await.result(p.future, 3.second)
    val f1 = Files.readAllBytes(from)
    val f2 = result
    Files.delete(to)
    assertEquals(f1.size, f2)
  }
} 
Example 33
Source File: implicits.scala    From frees-rpc-workshop   with Apache License 2.0 5 votes vote down vote up
package scalaexchange
package serverapp

import cats.{~>, Applicative}
import freestyle.rpc.server.handlers.GrpcServerHandler
import freestyle.rpc.server._
import freestyle.rpc.server.implicits._
import monix.eval.Task

import scalaexchange.services.protocol.RFMAnalysisService
import scalaexchange.services.runtime.RFMAnalysisServiceHandler

trait Implicits extends scalaexchange.CommonImplicits {

  implicit def rfmAnalisysServiceHandler[F[_]: Applicative](
      implicit T2F: Task ~> F): RFMAnalysisServiceHandler[F] =
    new RFMAnalysisServiceHandler[F]

  val grpcConfigs: List[GrpcConfig] = List(
    AddService(
      RFMAnalysisService.bindService[ConcurrentMonad]
    )
  )

  implicit def grpcServerHandler: GrpcServer.Op ~> ConcurrentMonad =
    new GrpcServerHandler[ConcurrentMonad] andThen
      new GrpcKInterpreter[ConcurrentMonad](ServerW(port, grpcConfigs).server)

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

import cats.data.NonEmptyList
import cats.effect._
import cats.syntax.functor._
import dev.profunktor.fs2rabbit.config.{Fs2RabbitConfig, Fs2RabbitNodeConfig}
import dev.profunktor.fs2rabbit.interpreter.RabbitClient
import dev.profunktor.fs2rabbit.resiliency.ResilientStream
import monix.eval.{Task, TaskApp}
import java.util.concurrent.Executors

object MonixAutoAckConsumer extends TaskApp {

  private val config: Fs2RabbitConfig = Fs2RabbitConfig(
    virtualHost = "/",
    nodes = NonEmptyList.one(
      Fs2RabbitNodeConfig(
        host = "127.0.0.1",
        port = 5672
      )
    ),
    username = Some("guest"),
    password = Some("guest"),
    ssl = false,
    connectionTimeout = 3,
    requeueOnNack = false,
    requeueOnReject = false,
    internalQueueSize = Some(500),
    automaticRecovery = true
  )

  val blockerResource =
    Resource
      .make(Task(Executors.newCachedThreadPool()))(es => Task(es.shutdown()))
      .map(Blocker.liftExecutorService)

  override def run(args: List[String]): Task[ExitCode] =
    blockerResource.use { blocker =>
      RabbitClient[Task](config, blocker).flatMap { client =>
        ResilientStream
          .runF(new AutoAckConsumerDemo[Task](client).program)
          .as(ExitCode.Success)
      }
    }

} 
Example 35
Source File: AdminRoutes.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.admin.routes

import akka.actor.ActorSystem
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import ch.epfl.bluebrain.nexus.admin.index.{OrganizationCache, ProjectCache}
import ch.epfl.bluebrain.nexus.admin.organizations.Organizations
import ch.epfl.bluebrain.nexus.admin.projects.Projects
import ch.epfl.bluebrain.nexus.iam.acls.Acls
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.routes.EventRoutes
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig.{HttpConfig, PaginationConfig, PersistenceConfig}
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global

object AdminRoutes {

  
  final def apply(
      orgs: Organizations[Task],
      projects: Projects[Task],
      orgCache: OrganizationCache[Task],
      projCache: ProjectCache[Task],
      acls: Acls[Task],
      realms: Realms[Task]
  )(implicit
      as: ActorSystem,
      cfg: ServiceConfig
  ): Route = {
    implicit val hc: HttpConfig        = cfg.http
    implicit val pc: PersistenceConfig = cfg.persistence
    implicit val pgc: PaginationConfig = cfg.pagination

    val eventsRoutes  = new EventRoutes(acls, realms).routes
    val orgRoutes     = new OrganizationRoutes(orgs, orgCache, acls, realms).routes
    val projectRoutes = new ProjectRoutes(projects, orgCache, projCache, acls, realms).routes

    pathPrefix(cfg.http.prefix) {
      eventsRoutes ~ orgRoutes ~ projectRoutes
    }
  }
} 
Example 36
Source File: PathDirectives.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.admin.directives

import java.util.UUID

import akka.http.scaladsl.server.Directive1
import akka.http.scaladsl.server.Directives._
import ch.epfl.bluebrain.nexus.admin.index.{OrganizationCache, ProjectCache}
import monix.eval.Task
import monix.execution.Scheduler

import scala.util.Try

object PathDirectives {

  
  def project(implicit cache: ProjectCache[Task], s: Scheduler): Directive1[(String, String)] =
    pathPrefix(Segment / Segment).tflatMap {
      case (orgSegment, projSegment) =>
        Try((UUID.fromString(orgSegment), UUID.fromString(projSegment)))
          .map {
            case (orgUuid, projUuid) =>
              onSuccess(cache.get(orgUuid, projUuid).runToFuture).flatMap {
                case Some(resource) => provide((resource.value.organizationLabel, resource.value.label))
                case None           => provide((orgSegment, projSegment))
              }
          }
          .getOrElse(provide((orgSegment, projSegment)))
    }

} 
Example 37
Source File: RepairFromMessages.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.admin

import java.util.UUID

import akka.actor.ActorSystem
import akka.persistence.cassandra.query.scaladsl.CassandraReadJournal
import akka.persistence.query.PersistenceQuery
import ch.epfl.bluebrain.nexus.admin.organizations.Organizations
import ch.epfl.bluebrain.nexus.admin.projects.Projects
import com.typesafe.scalalogging.Logger
import monix.eval.Task
import monix.execution.Scheduler

import scala.concurrent.Future
import scala.util.Try


object RepairFromMessages {

  private val log = Logger[RepairFromMessages.type]

  def repair(
      o: Organizations[Task],
      p: Projects[Task]
  )(implicit as: ActorSystem, sc: Scheduler): Future[Unit] = {
    val pq = PersistenceQuery(as).readJournalFor[CassandraReadJournal](CassandraReadJournal.Identifier)

    pq.currentPersistenceIds()
      .mapAsync(1) {
        case OrgId(uuid)  => (o.fetch(uuid) >> Task.unit).runToFuture
        case ProjId(uuid) => (p.fetch(uuid) >> Task.unit).runToFuture
        case other        =>
          log.warn(s"Unknown persistence id '$other'")
          Future.successful(())
      }
      .runFold(0) {
        case (acc, _) =>
          if (acc % 100 == 0) log.info(s"Processed '$acc' persistence ids.")
          acc + 1
      }
      .map(_ => ())
  }

  sealed abstract class PersistenceId(prefix: String) {
    private val len                        = prefix.length
    def unapply(arg: String): Option[UUID] =
      if (arg.startsWith(prefix)) Try(UUID.fromString(arg.drop(len))).toOption
      else None
  }
  object OrgId extends PersistenceId("organizations-")
  object ProjId extends PersistenceId("projects-")
} 
Example 38
Source File: EventRoutes.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.routes

import akka.actor.ActorSystem
import akka.http.scaladsl.marshalling.sse.EventStreamMarshalling._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import ch.epfl.bluebrain.nexus.admin.client.types.{Organization, Project}
import ch.epfl.bluebrain.nexus.iam.acls.Acls
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.types.{Caller, Permission}
import ch.epfl.bluebrain.nexus.kg.resources.Event.JsonLd._
import ch.epfl.bluebrain.nexus.rdf.Iri.Path._
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig
import ch.epfl.bluebrain.nexus.service.directives.AuthDirectives
import kamon.instrumentation.akka.http.TracingDirectives.operationName
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global

class EventRoutes(acls: Acls[Task], realms: Realms[Task], caller: Caller)(implicit
    override val as: ActorSystem,
    override val config: ServiceConfig
) extends AuthDirectives(acls, realms)
    with EventCommonRoutes {

  private val read: Permission = Permission.unsafe("resources/read")

  def routes(project: Project): Route = {

    lastEventId { offset =>
      operationName(s"/${config.http.prefix}/resources/{org}/{project}/events") {
        authorizeFor(project.organizationLabel / project.label, read)(caller) {
          complete(source(s"project=${project.uuid}", offset))
        }
      }
    }
  }

  def routes(org: Organization): Route =
    lastEventId { offset =>
      operationName(s"/${config.http.prefix}/resources/{org}/events") {
        authorizeFor(/ + org.label, read)(caller) {
          complete(source(s"org=${org.uuid}", offset))
        }
      }
    }
} 
Example 39
Source File: TagRoutes.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.routes

import akka.http.scaladsl.model.StatusCodes.{Created, OK}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import ch.epfl.bluebrain.nexus.admin.client.types.Project
import ch.epfl.bluebrain.nexus.iam.acls.Acls
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.types.Identity.Subject
import ch.epfl.bluebrain.nexus.iam.types.{Caller, Permission}
import ch.epfl.bluebrain.nexus.kg.config.Contexts.tagCtxUri
import ch.epfl.bluebrain.nexus.kg.directives.ProjectDirectives._
import ch.epfl.bluebrain.nexus.kg.marshallers.instances._
import ch.epfl.bluebrain.nexus.kg.resources._
import ch.epfl.bluebrain.nexus.kg.resources.syntax._
import ch.epfl.bluebrain.nexus.rdf.Iri.AbsoluteIri
import ch.epfl.bluebrain.nexus.rdf.Iri.Path._
import ch.epfl.bluebrain.nexus.rdf.implicits._
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig
import ch.epfl.bluebrain.nexus.service.directives.AuthDirectives
import io.circe.syntax._
import io.circe.{Encoder, Json}
import kamon.Kamon
import kamon.instrumentation.akka.http.TracingDirectives.operationName
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global

class TagRoutes private[routes] (
    resourceType: String,
    tags: Tags[Task],
    acls: Acls[Task],
    realms: Realms[Task],
    schema: Ref,
    write: Permission
)(implicit
    caller: Caller,
    project: Project,
    config: ServiceConfig
) extends AuthDirectives(acls, realms) {

  private val projectPath               = project.organizationLabel / project.label
  implicit private val subject: Subject = caller.subject

  
  def routes(id: AbsoluteIri): Route =
    // Consume the tag segment
    pathPrefix("tags") {
      concat(
        // Create tag
        (post & parameter("rev".as[Long]) & pathEndOrSingleSlash) { rev =>
          operationName(opName) {
            (authorizeFor(projectPath, write) & projectNotDeprecated) {
              entity(as[Json]) { source =>
                Kamon.currentSpan().tag("resource.operation", "create")
                complete(tags.create(Id(project.ref, id), rev, source, schema).value.runWithStatus(Created))
              }
            }
          }
        },
        // Fetch a tag
        (get & projectNotDeprecated & pathEndOrSingleSlash) {
          operationName(opName) {
            authorizeFor(projectPath, read)(caller) {
              parameter("rev".as[Long].?) {
                case Some(rev) => complete(tags.fetch(Id(project.ref, id), rev, schema).value.runWithStatus(OK))
                case _         => complete(tags.fetch(Id(project.ref, id), schema).value.runWithStatus(OK))
              }
            }
          }
        }
      )
    }

  implicit private def tagsEncoder: Encoder[TagSet] =
    Encoder.instance(tags => Json.obj("tags" -> Json.arr(tags.map(_.asJson).toSeq: _*)).addContext(tagCtxUri))

  private def opName: String                        =
    resourceType match {
      case "resources" => s"/${config.http.prefix}/resources/{org}/{project}/{schemaId}/{id}/tags"
      case _           => s"/${config.http.prefix}/$resourceType/{org}/{project}/{id}/tags"
    }
} 
Example 40
Source File: Clients.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.routes

import ch.epfl.bluebrain.nexus.admin.client.AdminClient
import ch.epfl.bluebrain.nexus.commons.es.client.ElasticSearchClient
import ch.epfl.bluebrain.nexus.commons.http.HttpClient
import ch.epfl.bluebrain.nexus.commons.http.HttpClient.UntypedHttpClient
import ch.epfl.bluebrain.nexus.commons.search.QueryResults
import ch.epfl.bluebrain.nexus.commons.sparql.client.BlazegraphClient
import ch.epfl.bluebrain.nexus.storage.client.StorageClient
import io.circe.Json
import monix.eval.Task


final case class Clients[F[_]]()(implicit
    val sparql: BlazegraphClient[F],
    val elasticSearch: ElasticSearchClient[F],
    val admin: AdminClient[F],
    val defaultRemoteStorage: StorageClient[F],
    val rsSearch: HttpClient[F, QueryResults[Json]],
    val http: UntypedHttpClient[Task],
    val uclJson: HttpClient[Task, Json]
)

object Clients {
  implicit def esClient[F[_]](implicit clients: Clients[F]): ElasticSearchClient[F]  = clients.elasticSearch
  implicit def sparqlClient[F[_]](implicit clients: Clients[F]): BlazegraphClient[F] = clients.sparql
} 
Example 41
Source File: ArchiveRoutes.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.routes

import akka.http.scaladsl.model.StatusCodes.{Created, OK}
import akka.http.scaladsl.model.headers.Accept
import akka.http.scaladsl.model.{HttpEntity, MediaTypes}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import ch.epfl.bluebrain.nexus.admin.client.types.Project
import ch.epfl.bluebrain.nexus.iam.acls.Acls
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.types.Caller
import ch.epfl.bluebrain.nexus.iam.types.Identity.Subject
import ch.epfl.bluebrain.nexus.kg.KgError.{InvalidOutputFormat, UnacceptedResponseContentType}
import ch.epfl.bluebrain.nexus.kg.archives.Archive._
import ch.epfl.bluebrain.nexus.kg.directives.PathDirectives._
import ch.epfl.bluebrain.nexus.kg.directives.ProjectDirectives._
import ch.epfl.bluebrain.nexus.kg.directives.QueryDirectives.outputFormat
import ch.epfl.bluebrain.nexus.kg.marshallers.instances._
import ch.epfl.bluebrain.nexus.kg.resources._
import ch.epfl.bluebrain.nexus.kg.resources.syntax._
import ch.epfl.bluebrain.nexus.kg.routes.OutputFormat.Tar
import ch.epfl.bluebrain.nexus.rdf.Iri.AbsoluteIri
import ch.epfl.bluebrain.nexus.rdf.Iri.Path._
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig
import ch.epfl.bluebrain.nexus.service.directives.AuthDirectives
import io.circe.Json
import kamon.instrumentation.akka.http.TracingDirectives.operationName
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global

class ArchiveRoutes private[routes] (archives: Archives[Task], acls: Acls[Task], realms: Realms[Task])(implicit
    project: Project,
    caller: Caller,
    config: ServiceConfig
) extends AuthDirectives(acls, realms) {

  private val responseType              = MediaTypes.`application/x-tar`
  private val projectPath               = project.organizationLabel / project.label
  implicit private val subject: Subject = caller.subject

  
  def routes(id: AbsoluteIri): Route = {
    val resId = Id(project.ref, id)
    concat(
      // Create archive
      (put & pathEndOrSingleSlash) {
        operationName(s"/${config.http.prefix}/archives/{org}/{project}/{id}") {
          (authorizeFor(projectPath, write) & projectNotDeprecated) {
            entity(as[Json]) { source =>
              complete(archives.create(resId, source).value.runWithStatus(Created))
            }
          }
        }
      },
      // Fetch archive
      (get & outputFormat(strict = true, Tar) & pathEndOrSingleSlash) {
        case Tar                           => getArchive(resId)
        case format: NonBinaryOutputFormat => getResource(resId)(format)
        case other                         => failWith(InvalidOutputFormat(other.toString))

      }
    )
  }

  private def getResource(resId: ResId)(implicit format: NonBinaryOutputFormat): Route =
    completeWithFormat(archives.fetch(resId).value.runWithStatus(OK))

  private def getArchive(resId: ResId): Route = {
    (parameter("ignoreNotFound".as[Boolean] ? false) & extractCallerAcls(anyProject)) { (ignoreNotFound, acls) =>
      onSuccess(archives.fetchArchive(resId, ignoreNotFound)(acls, caller).value.runToFuture) {
        case Right(source) =>
          headerValueByType[Accept](()) { accept =>
            if (accept.mediaRanges.exists(_.matches(responseType)))
              complete(HttpEntity(responseType, source))
            else
              failWith(
                UnacceptedResponseContentType(
                  s"File Media Type '$responseType' does not match the Accept header value '${accept.mediaRanges.mkString(", ")}'"
                )
              )
          }
        case Left(err)     => complete(err)
      }
    }
  }
} 
Example 42
Source File: GlobalEventRoutes.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.routes

import akka.actor.ActorSystem
import akka.http.scaladsl.marshalling.sse.EventStreamMarshalling._
import akka.http.scaladsl.server.Directives.complete
import akka.http.scaladsl.server.Route
import ch.epfl.bluebrain.nexus.iam.acls.Acls
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.types.{Caller, Permission}
import ch.epfl.bluebrain.nexus.kg.persistence.TaggingAdapter
import ch.epfl.bluebrain.nexus.kg.resources.Event.JsonLd._
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig
import ch.epfl.bluebrain.nexus.service.directives.AuthDirectives
import kamon.instrumentation.akka.http.TracingDirectives.operationName
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global

class GlobalEventRoutes(acls: Acls[Task], realms: Realms[Task], caller: Caller)(implicit
    override val as: ActorSystem,
    override val config: ServiceConfig
) extends AuthDirectives(acls, realms)
    with EventCommonRoutes {

  private val read: Permission = Permission.unsafe("events/read")

  def routes: Route =
    lastEventId { offset =>
      operationName(s"/${config.http.prefix}/events") {
        authorizeFor(permission = read)(caller) {
          complete(source(TaggingAdapter.EventTag, offset))
        }
      }
    }
} 
Example 43
Source File: Status.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.routes

import akka.cluster.{Cluster, MemberStatus}
import monix.eval.Task

sealed trait Status {

  
  def check: Task[Boolean]
}

object Status {

  class ClusterStatus(cluster: Cluster) extends Status {
    override def check: Task[Boolean] =
      Task.pure(
        !cluster.isTerminated &&
          cluster.state.leader.isDefined && cluster.state.members.nonEmpty &&
          !cluster.state.members.exists(_.status != MemberStatus.Up) && cluster.state.unreachable.isEmpty
      )
  }
} 
Example 44
Source File: ProjectDirectives.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.directives

import java.util.UUID

import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.{Directive0, Directive1}
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.client.AdminClient
import ch.epfl.bluebrain.nexus.admin.client.types.{Organization, Project}
import ch.epfl.bluebrain.nexus.iam.client.types.AuthToken
import ch.epfl.bluebrain.nexus.iam.types.Identity.Subject
import ch.epfl.bluebrain.nexus.kg.KgError.{OrganizationNotFound, ProjectIsDeprecated, ProjectNotFound}
import ch.epfl.bluebrain.nexus.kg.cache.ProjectCache
import ch.epfl.bluebrain.nexus.kg.config.Schemas
import ch.epfl.bluebrain.nexus.kg.resources.{OrganizationRef, ProjectInitializer}
import ch.epfl.bluebrain.nexus.kg.resources.ProjectIdentifier._
import ch.epfl.bluebrain.nexus.kg.resources.syntax._
import ch.epfl.bluebrain.nexus.rdf.Iri.AbsoluteIri
import ch.epfl.bluebrain.nexus.service.config.Vocabulary.nxv
import monix.eval.Task
import monix.execution.Scheduler

import scala.util.{Success, Try}

object ProjectDirectives {

  // TODO: Remove when migrating ADMIN client
  implicit private val fakeToken: Option[AuthToken] = None

  val defaultPrefixMapping: Map[String, AbsoluteIri] = Map(
    "resource"        -> Schemas.unconstrainedSchemaUri,
    "schema"          -> Schemas.shaclSchemaUri,
    "view"            -> Schemas.viewSchemaUri,
    "resolver"        -> Schemas.resolverSchemaUri,
    "file"            -> Schemas.fileSchemaUri,
    "storage"         -> Schemas.storageSchemaUri,
    "nxv"             -> nxv.base,
    "documents"       -> nxv.defaultElasticSearchIndex.value,
    "graph"           -> nxv.defaultSparqlIndex.value,
    "defaultResolver" -> nxv.defaultResolver.value,
    "defaultStorage"  -> nxv.defaultStorage.value
  )

  
  def projectNotDeprecated(implicit proj: Project): Directive0 =
    if (proj.deprecated) failWith(ProjectIsDeprecated(proj.projectLabel))
    else pass
} 
Example 45
Source File: ProjectAttributesCoordinator.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.async

import akka.actor.{ActorRef, ActorSystem}
import cats.effect.Async
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.client.types.Project
import ch.epfl.bluebrain.nexus.kg.async.ProjectAttributesCoordinatorActor.Msg._
import ch.epfl.bluebrain.nexus.kg.cache.ProjectCache
import ch.epfl.bluebrain.nexus.kg.resources.ProjectIdentifier.ProjectRef
import ch.epfl.bluebrain.nexus.kg.resources.syntax._
import ch.epfl.bluebrain.nexus.kg.resources.{Files, OrganizationRef}
import ch.epfl.bluebrain.nexus.kg.storage.Storage.StorageOperations.FetchAttributes
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig
import ch.epfl.bluebrain.nexus.sourcing.projections.Projections
import monix.eval.Task


  def stop(projectRef: ProjectRef): F[Unit] = {
    ref ! Stop(projectRef.id)
    F.unit
  }
}

object ProjectAttributesCoordinator {
  def apply(files: Files[Task], projectCache: ProjectCache[Task])(implicit
      config: ServiceConfig,
      fetchAttributes: FetchAttributes[Task],
      as: ActorSystem,
      P: Projections[Task, String]
  ): ProjectAttributesCoordinator[Task] = {
    val coordinatorRef = ProjectAttributesCoordinatorActor.start(files, None, config.cluster.shards)
    new ProjectAttributesCoordinator[Task](projectCache, coordinatorRef)
  }
} 
Example 46
Source File: RepairFromMessages.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg

import java.net.URLDecoder
import java.util.UUID

import akka.actor.ActorSystem
import akka.persistence.cassandra.query.scaladsl.CassandraReadJournal
import akka.persistence.query.PersistenceQuery
import ch.epfl.bluebrain.nexus.kg.resources.{Id, Repo, ResId}
import ch.epfl.bluebrain.nexus.kg.resources.ProjectIdentifier.ProjectRef
import ch.epfl.bluebrain.nexus.rdf.Iri
import com.typesafe.scalalogging.Logger
import monix.eval.Task
import monix.execution.Scheduler
import monix.execution.schedulers.CanBlock

import scala.concurrent.Future
import scala.util.Try


object RepairFromMessages {
  // $COVERAGE-OFF$

  private val log = Logger[RepairFromMessages.type]

  def repair(repo: Repo[Task])(implicit as: ActorSystem, sc: Scheduler, pm: CanBlock): Unit = {
    log.info("Repairing dependent tables from messages.")
    val pq = PersistenceQuery(as).readJournalFor[CassandraReadJournal](CassandraReadJournal.Identifier)
    Task
      .fromFuture {
        pq.currentPersistenceIds()
          .mapAsync(1) {
            case ResourceId(id) => (repo.get(id, None).value >> Task.unit).runToFuture
            case other          =>
              log.warn(s"Unknown persistence id '$other'")
              Future.successful(())
          }
          .runFold(0) {
            case (acc, _) =>
              if (acc % 1000 == 0) log.info(s"Processed '$acc' persistence ids.")
              acc + 1
          }
          .map(_ => ())
      }
      .runSyncUnsafe()
    log.info("Finished repairing dependent tables from messages.")
  }

  object ResourceId {
    private val regex                       =
      "^resources\\-([0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12})\\-(.+)$".r
    def unapply(arg: String): Option[ResId] =
      arg match {
        case regex(stringUuid, stringId) =>
          for {
            uuid <- Try(UUID.fromString(stringUuid)).toOption
            iri  <- Iri.absolute(URLDecoder.decode(stringId, "UTF-8")).toOption
          } yield Id(ProjectRef(uuid), iri)
        case _                           => None
      }
  }
  // $COVERAGE-ON$
} 
Example 47
Source File: IamRoutes.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.iam.routes

import akka.actor.ActorSystem
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import ch.epfl.bluebrain.nexus.iam.acls.Acls
import ch.epfl.bluebrain.nexus.iam.permissions.Permissions
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig.{HttpConfig, PersistenceConfig}
import monix.eval.Task

object IamRoutes {

  final def apply(
      acls: Acls[Task],
      realms: Realms[Task],
      perms: Permissions[Task]
  )(implicit as: ActorSystem, cfg: ServiceConfig): Route = {
    implicit val hc: HttpConfig        = cfg.http
    implicit val pc: PersistenceConfig = cfg.persistence

    val eventsRoutes = new EventRoutes(acls, realms).routes
    val idsRoutes    = new IdentitiesRoutes(acls, realms).routes
    val permsRoutes  = new PermissionsRoutes(perms, acls, realms).routes
    val realmsRoutes = new RealmsRoutes(acls, realms).routes
    val aclsRoutes   = new AclsRoutes(acls, realms).routes

    pathPrefix(cfg.http.prefix) {
      eventsRoutes ~ aclsRoutes ~ permsRoutes ~ realmsRoutes ~ idsRoutes
    }
  }
} 
Example 48
Source File: PermissionsRoutes.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.iam.routes

import akka.http.javadsl.server.Rejections._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import ch.epfl.bluebrain.nexus.iam.acls.Acls
import ch.epfl.bluebrain.nexus.iam.permissions.Permissions
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.routes.PermissionsRoutes.PatchPermissions
import ch.epfl.bluebrain.nexus.iam.routes.PermissionsRoutes.PatchPermissions.{Append, Replace, Subtract}
import ch.epfl.bluebrain.nexus.iam.types.Permission
import ch.epfl.bluebrain.nexus.iam.types.ResourceF._
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig.HttpConfig
import ch.epfl.bluebrain.nexus.service.directives.AuthDirectives
import ch.epfl.bluebrain.nexus.service.marshallers.instances._
import io.circe.{Decoder, DecodingFailure}
import kamon.instrumentation.akka.http.TracingDirectives.operationName
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global


class PermissionsRoutes(permissions: Permissions[Task], acls: Acls[Task], realms: Realms[Task])(implicit
    http: HttpConfig
) extends AuthDirectives(acls, realms) {

  def routes: Route =
    (pathPrefix("permissions") & pathEndOrSingleSlash) {
      operationName(s"/${http.prefix}/permissions") {
        extractCaller { implicit caller =>
          concat(
            get {
              parameter("rev".as[Long].?) {
                case Some(rev) => complete(permissions.fetchAt(rev).runNotFound)
                case None      => complete(permissions.fetch.runToFuture)
              }
            },
            (put & parameter("rev" ? 0L)) { rev =>
              entity(as[PatchPermissions]) {
                case Replace(set) =>
                  complete(permissions.replace(set, rev).runToFuture)
                case _            => reject(validationRejection("Only @type 'Replace' is permitted when using 'put'."))
              }
            },
            delete {
              parameter("rev".as[Long]) { rev => complete(permissions.delete(rev).runToFuture) }
            },
            (patch & parameter("rev" ? 0L)) { rev =>
              entity(as[PatchPermissions]) {
                case Append(set)   =>
                  complete(permissions.append(set, rev).runToFuture)
                case Subtract(set) =>
                  complete(permissions.subtract(set, rev).runToFuture)
                case _             =>
                  reject(validationRejection("Only @type 'Append' or 'Subtract' is permitted when using 'patch'."))
              }
            }
          )
        }
      }
    }
}

object PermissionsRoutes {

  sealed private[routes] trait PatchPermissions extends Product with Serializable

  private[routes] object PatchPermissions {

    final case class Append(permissions: Set[Permission])   extends PatchPermissions
    final case class Subtract(permissions: Set[Permission]) extends PatchPermissions
    final case class Replace(permissions: Set[Permission])  extends PatchPermissions

    implicit val patchPermissionsDecoder: Decoder[PatchPermissions] =
      Decoder.instance { hc =>
        for {
          permissions <- hc.get[Set[Permission]]("permissions")
          tpe          = hc.get[String]("@type").getOrElse("Replace")
          patch       <- tpe match {
                           case "Replace"  => Right(Replace(permissions))
                           case "Append"   => Right(Append(permissions))
                           case "Subtract" => Right(Subtract(permissions))
                           case _          => Left(DecodingFailure("@type field must have Append or Subtract value", hc.history))
                         }
        } yield patch
      }
  }

} 
Example 49
Source File: IdentitiesRoutes.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.iam.routes

import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import ch.epfl.bluebrain.nexus.iam.acls.Acls
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.types.Caller.JsonLd._
import ch.epfl.bluebrain.nexus.service.directives.AuthDirectives
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig.HttpConfig
import ch.epfl.bluebrain.nexus.service.marshallers.instances._
import kamon.instrumentation.akka.http.TracingDirectives.operationName
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global


class IdentitiesRoutes(acls: Acls[Task], realms: Realms[Task])(implicit http: HttpConfig)
    extends AuthDirectives(acls, realms) {

  def routes: Route = {
    (pathPrefix("identities") & pathEndOrSingleSlash) {
      operationName(s"/${http.prefix}/identities") {
        (extractCaller & get) { caller =>
          complete(caller)
        }
      }
    }
  }
} 
Example 50
Source File: RepairFromMessages.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.iam

import java.net.URLDecoder

import akka.actor.ActorSystem
import akka.persistence.cassandra.query.scaladsl.CassandraReadJournal
import akka.persistence.query.PersistenceQuery
import ch.epfl.bluebrain.nexus.iam.acls.Acls
import ch.epfl.bluebrain.nexus.iam.permissions.Permissions
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.types.Label
import ch.epfl.bluebrain.nexus.rdf.Iri.Path
import com.typesafe.scalalogging.Logger
import monix.eval.Task
import monix.execution.Scheduler
import monix.execution.schedulers.CanBlock

import scala.concurrent.Future


object RepairFromMessages {
  // $COVERAGE-OFF$

  private val log = Logger[RepairFromMessages.type]

  def repair(
      p: Permissions[Task],
      r: Realms[Task],
      a: Acls[Task]
  )(implicit as: ActorSystem, sc: Scheduler, pm: CanBlock): Unit = {
    val pq = PersistenceQuery(as).readJournalFor[CassandraReadJournal](CassandraReadJournal.Identifier)

    pq.currentPersistenceIds()
      .mapAsync(1) {
        case PermissionsId() => p.agg.currentState(p.persistenceId).runToFuture
        case RealmId(label)  => r.agg.currentState(label.value).runToFuture
        case AclId(path)     => a.agg.currentState(path.asString).runToFuture
        case other           =>
          log.warn(s"Unknown persistence id '$other'")
          Future.successful(())
      }
      .runFold(0) {
        case (acc, _) =>
          if (acc % 100 == 0) log.info(s"Processed '$acc' persistence ids.")
          acc + 1
      }
      .runSyncDiscard()

    log.info("Repair from messages table completed.")
  }

  sealed abstract class PersistenceId(prefix: String) {
    private val len                                       = prefix.length
    protected def dropPrefix(arg: String): Option[String] =
      if (arg.startsWith(prefix)) Some(arg.drop(len))
      else None
  }
  object RealmId extends PersistenceId("realms-") {
    def unapply(arg: String): Option[Label] =
      dropPrefix(arg).map(Label.unsafe)
  }
  object AclId extends PersistenceId("acls-") {
    def unapply(arg: String): Option[Path] =
      dropPrefix(arg).flatMap(str => Path(URLDecoder.decode(str, "UTF-8")).toOption)
  }
  object PermissionsId                                {
    def unapply(arg: String): Boolean =
      arg == "permissions-permissions"
  }

  implicit class RichFuture[A](val future: Future[A]) extends AnyVal {
    def runSyncDiscard()(implicit s: Scheduler, permit: CanBlock): Unit =
      Task.fromFuture(future).map(_ => ()).runSyncUnsafe()
  }
  // $COVERAGE-ON$
} 
Example 51
Source File: AuthDirectives.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.service.directives

import akka.http.scaladsl.model.headers.OAuth2BearerToken
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.directives.Credentials
import akka.http.scaladsl.server.directives.FutureDirectives.onComplete
import akka.http.scaladsl.server.{Directive0, Directive1}
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.exceptions.AdminError.AuthorizationFailed
import ch.epfl.bluebrain.nexus.iam.acls.{AccessControlLists, Acls}
import ch.epfl.bluebrain.nexus.iam.auth.AccessToken
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.types.IamError.InvalidAccessToken
import ch.epfl.bluebrain.nexus.iam.types.{Caller, Permission}
import ch.epfl.bluebrain.nexus.kg.KgError.AuthenticationFailed
import ch.epfl.bluebrain.nexus.rdf.Iri.{AbsoluteIri, Path}
import ch.epfl.bluebrain.nexus.rdf.Iri.Path._
import ch.epfl.bluebrain.nexus.rdf.implicits._
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig.HttpConfig
import ch.epfl.bluebrain.nexus.service.exceptions.ServiceError.InternalError
import com.typesafe.scalalogging.Logger
import monix.eval.Task
import monix.execution.Scheduler

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


  def extractCallerAcls(path: Path)(implicit c: Caller): Directive1[AccessControlLists] =
    onComplete(acls.list(path, ancestors = true, self = true).runToFuture).flatMap {
      case Success(AccessControlLists.empty) => failWith(AuthorizationFailed)
      case Success(result)                   => provide(result)
      case Failure(err)                      =>
        val message = "Error when trying to check for permissions"
        logger.error(message, err)
        failWith(InternalError(message))
    }
} 
Example 52
Source File: instances.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.service.marshallers

import akka.http.scaladsl.marshalling.GenericMarshallers.eitherMarshaller
import akka.http.scaladsl.marshalling._
import akka.http.scaladsl.model.MediaTypes.`application/json`
import akka.http.scaladsl.model._
import ch.epfl.bluebrain.nexus.admin.exceptions.AdminError
import ch.epfl.bluebrain.nexus.admin.organizations.OrganizationRejection
import ch.epfl.bluebrain.nexus.admin.projects.ProjectRejection
import ch.epfl.bluebrain.nexus.commons.circe.syntax._
import ch.epfl.bluebrain.nexus.commons.http.JsonLdCirceSupport.OrderedKeys
import ch.epfl.bluebrain.nexus.commons.http.RdfMediaTypes._
import ch.epfl.bluebrain.nexus.commons.http.directives.StatusFrom
import ch.epfl.bluebrain.nexus.iam.acls.AclRejection
import ch.epfl.bluebrain.nexus.iam.permissions.PermissionsRejection
import ch.epfl.bluebrain.nexus.iam.realms.RealmRejection
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig.orderedKeys
import ch.epfl.bluebrain.nexus.service.exceptions.ServiceError
import ch.epfl.bluebrain.nexus.service.exceptions.ServiceError.InternalError
import ch.epfl.bluebrain.nexus.service.routes.ResourceRejection
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport
import io.circe._
import io.circe.syntax._
import monix.eval.Task
import monix.execution.Scheduler

import scala.collection.immutable.Seq
import scala.concurrent.Future
import scala.concurrent.duration.FiniteDuration

object instances extends FailFastCirceSupport {

  implicit val finiteDurationEncoder: Encoder[FiniteDuration] =
    Encoder.encodeString.contramap(fd => s"${fd.toMillis} ms")

  implicit val resourceRejectionEncoder: Encoder[ResourceRejection] =
    Encoder.instance {
      case r: ProjectRejection      => Encoder[ProjectRejection].apply(r)
      case r: OrganizationRejection => Encoder[OrganizationRejection].apply(r)
      case r: AclRejection          => Encoder[AclRejection].apply(r)
      case r: RealmRejection        => Encoder[RealmRejection].apply(r)
      case r: PermissionsRejection  => Encoder[PermissionsRejection].apply(r)
      case _                        => Encoder[ServiceError].apply(InternalError("unspecified"))
    }

  implicit val resourceRejectionStatusFrom: StatusFrom[ResourceRejection] =
    StatusFrom {
      case r: OrganizationRejection => OrganizationRejection.organizationStatusFrom(r)
      case r: ProjectRejection      => ProjectRejection.projectStatusFrom(r)
      case r: AclRejection          => AclRejection.aclRejectionStatusFrom(r)
      case r: RealmRejection        => RealmRejection.realmRejectionStatusFrom(r)
      case r: PermissionsRejection  => PermissionsRejection.permissionsRejectionStatusFrom(r)
    }

  override def unmarshallerContentTypes: Seq[ContentTypeRange] =
    List(`application/json`, `application/ld+json`, `application/sparql-results+json`)

  
  implicit final def rejection[A <: ResourceRejection: Encoder](implicit
      statusFrom: StatusFrom[A],
      printer: Printer = Printer.noSpaces.copy(dropNullValues = true),
      ordered: OrderedKeys = orderedKeys
  ): ToResponseMarshaller[A] = {
    val marshallers = Seq(`application/ld+json`, `application/json`).map { contentType =>
      Marshaller.withFixedContentType[A, HttpResponse](contentType) { rejection =>
        HttpResponse(
          status = statusFrom(rejection),
          entity = HttpEntity(contentType, printer.print(rejection.asJson.sortKeys))
        )
      }
    }
    Marshaller.oneOf(marshallers: _*)
  }

  implicit class EitherTask[R <: ResourceRejection, A](task: Task[Either[R, A]])(implicit s: Scheduler) {
    def runWithStatus(code: StatusCode): Future[Either[R, (StatusCode, A)]] =
      task.map(_.map(code -> _)).runToFuture
  }

  implicit class OptionTask[A](task: Task[Option[A]])(implicit s: Scheduler) {
    def runNotFound: Future[A] =
      task.flatMap {
        case Some(a) => Task.pure(a)
        case None    => Task.raiseError(AdminError.NotFound)
      }.runToFuture
  }
} 
Example 53
Source File: EventRoutesSpec.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.routes

import akka.NotUsed
import akka.actor.ActorSystem
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.model.headers.`Last-Event-ID`
import akka.http.scaladsl.model.sse.ServerSentEvent
import akka.persistence.query.{EventEnvelope, NoOffset, Offset, Sequence}
import akka.stream.scaladsl.Source
import ch.epfl.bluebrain.nexus.iam.acls.Acls
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.types.{Caller, Permission}
import ch.epfl.bluebrain.nexus.kg.resources.Event
import ch.epfl.bluebrain.nexus.kg.routes.EventRoutesSpec.TestableEventRoutes
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig
import ch.epfl.bluebrain.nexus.rdf.Iri.Path._
import io.circe.Encoder
import monix.eval.Task

class EventRoutesSpec extends EventsSpecBase {

  private val aclsApi = mock[Acls[Task]]
  private val realms  = mock[Realms[Task]]

  val eventRoutes = new TestableEventRoutes(events, aclsApi, realms, caller)

  "EventRoutes" should {
    val read = Permission.unsafe("resources/read")
    aclsApi.hasPermission("org" / "project", read)(caller) shouldReturn Task.pure(true)
    aclsApi.hasPermission(/ + "org", read)(caller) shouldReturn Task.pure(true)

    "return all events for a project" in {
      Get("/") ~> eventRoutes.routes(project) ~> check {
        val expected = jsonContentOf("/events/events.json").asArray.value
        status shouldEqual StatusCodes.OK
        responseAs[String] shouldEqual eventStreamFor(expected)
      }
    }

    "return all events for a project from the last seen" in {
      Get("/").addHeader(`Last-Event-ID`(0.toString)) ~> eventRoutes.routes(project) ~> check {
        val expected = jsonContentOf("/events/events.json").asArray.value
        status shouldEqual StatusCodes.OK
        responseAs[String] shouldEqual eventStreamFor(expected, 1)
      }
    }

    "return all events for an organization" in {
      Get("/") ~> eventRoutes.routes(organization) ~> check {
        val expected = jsonContentOf("/events/events.json").asArray.value
        status shouldEqual StatusCodes.OK
        responseAs[String] shouldEqual eventStreamFor(expected)
      }
    }

    "return all events for an organization from the last seen" in {
      Get("/").addHeader(`Last-Event-ID`(0.toString)) ~> eventRoutes.routes(organization) ~> check {
        val expected = jsonContentOf("/events/events.json").asArray.value
        status shouldEqual StatusCodes.OK
        responseAs[String] shouldEqual eventStreamFor(expected, 1)
      }
    }
  }

}

object EventRoutesSpec {

  class TestableEventRoutes(events: List[Event], acls: Acls[Task], realms: Realms[Task], caller: Caller)(implicit
      as: ActorSystem,
      config: ServiceConfig
  ) extends EventRoutes(acls, realms, caller) {

    private val envelopes = events.zipWithIndex.map {
      case (ev, idx) =>
        EventEnvelope(Sequence(idx.toLong), "persistenceid", 1L, ev, 1L)
    }

    override protected def source(
        tag: String,
        offset: Offset
    )(implicit enc: Encoder[Event]): Source[ServerSentEvent, NotUsed] = {
      val toDrop = offset match {
        case NoOffset    => 0
        case Sequence(v) => v + 1
      }
      Source(envelopes).drop(toDrop).flatMapConcat(ee => Source(eventToSse(ee).toList))
    }
  }
} 
Example 54
Source File: GlobalEventRoutesSpec.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.routes

import akka.NotUsed
import akka.actor.ActorSystem
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.model.headers.`Last-Event-ID`
import akka.http.scaladsl.model.sse.ServerSentEvent
import akka.persistence.query.{EventEnvelope, NoOffset, Offset, Sequence}
import akka.stream.scaladsl.Source
import ch.epfl.bluebrain.nexus.iam.acls.Acls
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.types.Caller
import ch.epfl.bluebrain.nexus.kg.resources.Event
import ch.epfl.bluebrain.nexus.kg.routes.GlobalEventRoutesSpec.TestableEventRoutes
import ch.epfl.bluebrain.nexus.rdf.Iri.Path
import ch.epfl.bluebrain.nexus.service.config.ServiceConfig
import io.circe.Encoder
import monix.eval.Task

class GlobalEventRoutesSpec extends EventsSpecBase {

  private val aclsApi = mock[Acls[Task]]
  private val realms  = mock[Realms[Task]]

  val routes = new TestableEventRoutes(events, aclsApi, realms, caller).routes
  aclsApi.hasPermission(Path./, read)(caller) shouldReturn Task.pure(true)

  "GlobalEventRoutes" should {

    "return all events for a project" in {
      Get("/") ~> routes ~> check {
        val expected = jsonContentOf("/events/events.json").asArray.value
        status shouldEqual StatusCodes.OK
        responseAs[String] shouldEqual eventStreamFor(expected)
      }
    }

    "return all events for a project from the last seen" in {
      Get("/").addHeader(`Last-Event-ID`(0.toString)) ~> routes ~> check {
        val expected = jsonContentOf("/events/events.json").asArray.value
        status shouldEqual StatusCodes.OK
        responseAs[String] shouldEqual eventStreamFor(expected, 1)
      }
    }
  }
}

object GlobalEventRoutesSpec {

  class TestableEventRoutes(events: List[Event], acls: Acls[Task], realms: Realms[Task], caller: Caller)(implicit
      as: ActorSystem,
      config: ServiceConfig
  ) extends GlobalEventRoutes(acls, realms, caller) {

    private val envelopes = events.zipWithIndex.map {
      case (ev, idx) =>
        EventEnvelope(Sequence(idx.toLong), "persistenceid", 1L, ev, 1L)
    }

    override protected def source(
        tag: String,
        offset: Offset
    )(implicit enc: Encoder[Event]): Source[ServerSentEvent, NotUsed] = {
      val toDrop = offset match {
        case NoOffset    => 0
        case Sequence(v) => v + 1
      }
      Source(envelopes).drop(toDrop).flatMapConcat(ee => Source(eventToSse(ee).toList))
    }
  }
} 
Example 55
Source File: ResolverCacheSpec.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.cache

import akka.actor.ExtendedActorSystem
import akka.serialization.Serialization
import akka.testkit._
import ch.epfl.bluebrain.nexus.commons.test.ActorSystemFixture
import ch.epfl.bluebrain.nexus.iam.types.Identity.Anonymous
import ch.epfl.bluebrain.nexus.kg.TestHelper
import ch.epfl.bluebrain.nexus.kg.config.KgConfig._
import ch.epfl.bluebrain.nexus.kg.resolve.Resolver._
import ch.epfl.bluebrain.nexus.kg.resources.ProjectIdentifier.{ProjectLabel, ProjectRef}
import ch.epfl.bluebrain.nexus.service.config.{ServiceConfig, Settings}
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.matchers.should.Matchers
import org.scalatest.{Inspectors, TryValues}

import scala.concurrent.duration._

//noinspection NameBooleanParameters
class ResolverCacheSpec
    extends ActorSystemFixture("ResolverCacheSpec", true)
    with Matchers
    with Inspectors
    with ScalaFutures
    with TryValues
    with TestHelper {

  implicit override def patienceConfig: PatienceConfig = PatienceConfig(3.seconds.dilated, 5.milliseconds)

  implicit private val appConfig: ServiceConfig = Settings(system).serviceConfig
  implicit private val keyValueStoreCfg         = appConfig.kg.keyValueStore.keyValueStoreConfig

  val ref1 = ProjectRef(genUUID)
  val ref2 = ProjectRef(genUUID)

  val label1 = ProjectLabel(genString(), genString())
  val label2 = ProjectLabel(genString(), genString())

  val resolver: InProjectResolver       = InProjectResolver(ref1, genIri, 1L, false, 10)
  val crossRefs: CrossProjectResolver   =
    CrossProjectResolver(Set(genIri), List(ref1, ref2), Set(Anonymous), ref1, genIri, 0L, false, 1)
  val crossLabels: CrossProjectResolver =
    CrossProjectResolver(Set(genIri), List(label1, label2), Set(Anonymous), ref1, genIri, 0L, false, 1)

  val resolverProj1: Set[InProjectResolver] = List.fill(5)(resolver.copy(id = genIri)).toSet
  val resolverProj2: Set[InProjectResolver] = List.fill(5)(resolver.copy(id = genIri, ref = ref2)).toSet

  private val cache = ResolverCache[Task]

  "ResolverCache" should {

    "index resolvers" in {
      val list = (resolverProj1 ++ resolverProj2).toList
      forAll(list) { resolver =>
        cache.put(resolver).runToFuture.futureValue
        cache.get(resolver.ref, resolver.id).runToFuture.futureValue shouldEqual Some(resolver)
      }
    }

    "list resolvers" in {
      cache.get(ref1).runToFuture.futureValue should contain theSameElementsAs resolverProj1
      cache.get(ref2).runToFuture.futureValue should contain theSameElementsAs resolverProj2
    }

    "deprecate resolver" in {
      val resolver = resolverProj1.head
      cache.put(resolver.copy(deprecated = true, rev = 2L)).runToFuture.futureValue
      cache.get(resolver.ref, resolver.id).runToFuture.futureValue shouldEqual None
      cache.get(ref1).runToFuture.futureValue should contain theSameElementsAs resolverProj1.filterNot(_ == resolver)
    }

    "serialize cross project resolver" when {
      val serialization = new Serialization(system.asInstanceOf[ExtendedActorSystem])
      "parameterized with ProjectRef" in {
        val bytes = serialization.serialize(crossRefs).success.value
        val out   = serialization.deserialize(bytes, classOf[CrossProjectResolver]).success.value
        out shouldEqual crossRefs
      }
      "parameterized with ProjectLabel" in {
        val bytes = serialization.serialize(crossLabels).success.value
        val out   = serialization.deserialize(bytes, classOf[CrossProjectResolver]).success.value
        out shouldEqual crossLabels
      }
    }
  }
} 
Example 56
Source File: StorageCacheSpec.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.cache

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

import akka.testkit._
import ch.epfl.bluebrain.nexus.commons.test.ActorSystemFixture
import ch.epfl.bluebrain.nexus.kg.TestHelper
import ch.epfl.bluebrain.nexus.kg.resources.ProjectIdentifier.ProjectRef
import ch.epfl.bluebrain.nexus.kg.storage.Storage.DiskStorage
import ch.epfl.bluebrain.nexus.rdf.implicits._
import ch.epfl.bluebrain.nexus.service.config.{ServiceConfig, Settings}
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.matchers.should.Matchers
import org.scalatest.{Inspectors, TryValues}

import scala.concurrent.duration._

//noinspection NameBooleanParameters
class StorageCacheSpec
    extends ActorSystemFixture("StorageCacheSpec", true)
    with Matchers
    with Inspectors
    with ScalaFutures
    with TryValues
    with TestHelper {

  implicit override def patienceConfig: PatienceConfig = PatienceConfig(3.seconds.dilated, 5.milliseconds)

  implicit private val clock: Clock             = Clock.systemUTC
  implicit private val appConfig: ServiceConfig = Settings(system).serviceConfig
  implicit private val keyValueStoreCfg         = appConfig.kg.keyValueStore.keyValueStoreConfig

  val ref1 = ProjectRef(genUUID)
  val ref2 = ProjectRef(genUUID)

  val time   = clock.instant()
  val lastId = url"http://example.com/lastA"
  // initialInstant.minusSeconds(1L + genInt().toLong)

  val tempStorage = DiskStorage(ref1, genIri, 1L, false, true, "alg", Paths.get("/tmp"), read, write, 1024L)

  val lastStorageProj1 = tempStorage.copy(id = lastId)
  val lastStorageProj2 = tempStorage.copy(ref = ref2, id = lastId)

  val storagesProj1: List[DiskStorage] = List.fill(5)(tempStorage.copy(id = genIri)) :+ lastStorageProj1
  val storagesProj2: List[DiskStorage] = List.fill(5)(tempStorage.copy(ref = ref2, id = genIri)) :+ lastStorageProj2

  private val cache = StorageCache[Task]

  "StorageCache" should {

    "index storages" in {
      forAll((storagesProj1 ++ storagesProj2).zipWithIndex) {
        case (storage, index) =>
          implicit val instant = time.plusSeconds(index.toLong)
          cache.put(storage).runToFuture.futureValue
          cache.get(storage.ref, storage.id).runToFuture.futureValue shouldEqual Some(storage)
      }
    }

    "get latest default storage" in {
      cache.getDefault(ref1).runToFuture.futureValue shouldEqual Some(lastStorageProj1)
      cache.getDefault(ref2).runToFuture.futureValue shouldEqual Some(lastStorageProj2)
      cache.getDefault(ProjectRef(genUUID)).runToFuture.futureValue shouldEqual None
    }

    "list storages" in {
      cache.get(ref1).runToFuture.futureValue should contain theSameElementsAs storagesProj1
      cache.get(ref2).runToFuture.futureValue should contain theSameElementsAs storagesProj2
    }

    "deprecate storage" in {
      val storage          = storagesProj1.head
      implicit val instant = time.plusSeconds(30L)
      cache.put(storage.copy(deprecated = true, rev = 2L)).runToFuture.futureValue
      cache.get(storage.ref, storage.id).runToFuture.futureValue shouldEqual None
      cache.get(ref1).runToFuture.futureValue should contain theSameElementsAs storagesProj1.filterNot(_ == storage)
    }
  }
} 
Example 57
Source File: IdentitiesRoutesSpec.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.iam.routes

import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.model.headers.OAuth2BearerToken
import akka.http.scaladsl.testkit.ScalatestRouteTest
import ch.epfl.bluebrain.nexus.iam.acls.Acls
import ch.epfl.bluebrain.nexus.iam.auth.{AccessToken, TokenRejection}
import ch.epfl.bluebrain.nexus.iam.realms._
import ch.epfl.bluebrain.nexus.iam.testsyntax._
import ch.epfl.bluebrain.nexus.iam.types.Caller
import ch.epfl.bluebrain.nexus.iam.types.IamError.InvalidAccessToken
import ch.epfl.bluebrain.nexus.iam.types.Identity.{Anonymous, Authenticated, User}
import ch.epfl.bluebrain.nexus.service.config.Settings
import ch.epfl.bluebrain.nexus.service.marshallers.instances._
import ch.epfl.bluebrain.nexus.service.routes.Routes
import ch.epfl.bluebrain.nexus.util.Resources
import com.typesafe.config.{Config, ConfigFactory}
import io.circe.Json
import monix.eval.Task
import org.mockito.matchers.MacroBasedMatchers
import org.mockito.{IdiomaticMockito, Mockito}
import org.scalatest.BeforeAndAfter
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike

import scala.concurrent.duration._

//noinspection TypeAnnotation
class IdentitiesRoutesSpec
    extends AnyWordSpecLike
    with Matchers
    with ScalatestRouteTest
    with BeforeAndAfter
    with MacroBasedMatchers
    with Resources
    with ScalaFutures
    with IdiomaticMockito {

  implicit override def patienceConfig: PatienceConfig = PatienceConfig(3.seconds, 100.milliseconds)

  override def testConfig: Config = ConfigFactory.load("test.conf")

  private val config        = Settings(system).serviceConfig
  implicit private val http = config.http

  private val realms: Realms[Task] = mock[Realms[Task]]
  private val acls: Acls[Task]     = mock[Acls[Task]]

  before {
    Mockito.reset(realms, acls)
  }

  "The IdentitiesRoutes" should {
    val routes = Routes.wrap(new IdentitiesRoutes(acls, realms).routes)
    "return forbidden" in {
      val err = InvalidAccessToken(TokenRejection.InvalidAccessToken)
      realms.caller(any[AccessToken]) shouldReturn Task.raiseError(err)
      Get("/identities").addCredentials(OAuth2BearerToken("token")) ~> routes ~> check {
        status shouldEqual StatusCodes.Unauthorized
      }
    }
    "return anonymous" in {
      realms.caller(any[AccessToken]) shouldReturn Task.pure(Caller.anonymous)
      Get("/identities") ~> routes ~> check {
        status shouldEqual StatusCodes.OK
        responseAs[Json].sort shouldEqual jsonContentOf("/identities/anonymous.json")
      }
    }
    "return all identities" in {
      val user   = User("theuser", "therealm")
      val auth   = Authenticated("therealm")
      val caller = Caller(user, Set(user, Anonymous, auth))
      realms.caller(any[AccessToken]) shouldReturn Task.pure(caller)
      Get("/identities").addCredentials(OAuth2BearerToken("token")) ~> routes ~> check {
        status shouldEqual StatusCodes.OK
        responseAs[Json].sort shouldEqual jsonContentOf("/identities/identities.json")
      }
    }
  }
} 
Example 58
Source File: Main.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.cli

import cats.Parallel
import cats.effect.{ContextShift, ExitCode, Timer}
import cats.syntax.all._
import monix.catnap.SchedulerEffect
import monix.eval.{Task, TaskApp}

// $COVERAGE-OFF$
object Main extends TaskApp {

  override def run(args: List[String]): Task[ExitCode] = {
    implicit val cs: ContextShift[Task] = SchedulerEffect.contextShift[Task](scheduler)
    implicit val tm: Timer[Task]        = SchedulerEffect.timer[Task](scheduler)
    implicit val pl: Parallel[Task]     = Task.catsParallel
    Cli(args, sys.env).recoverWith {
      case err: CliError => Task.delay(println(err.show)).as(ExitCode.Error)
    }
  }

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

import akka.http.scaladsl.marshalling.GenericMarshallers.eitherMarshaller
import akka.http.scaladsl.marshalling._
import akka.http.scaladsl.model.MediaTypes._
import akka.http.scaladsl.model._
import ch.epfl.bluebrain.nexus.commons.http.RdfMediaTypes._
import ch.epfl.bluebrain.nexus.storage.JsonLdCirceSupport.sortKeys
import ch.epfl.bluebrain.nexus.storage.JsonLdCirceSupport.OrderedKeys
import ch.epfl.bluebrain.nexus.storage.Rejection
import ch.epfl.bluebrain.nexus.storage.config.AppConfig._
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport
import io.circe._
import io.circe.syntax._
import monix.eval.Task
import monix.execution.Scheduler

import scala.collection.immutable.Seq
import scala.concurrent.Future

object instances extends LowPriority {

  
  implicit final def valueWithStatusCodeMarshaller[A: Encoder](implicit
      printer: Printer = defaultPrinter,
      keys: OrderedKeys = orderedKeys
  ): ToResponseMarshaller[(StatusCode, A)] =
    jsonLdWithStatusCodeMarshaller.compose { case (status, value) => status -> value.asJson }

  private[routes] def onOf[A, Response](
      fMarshaller: MediaType.WithFixedCharset => Marshaller[A, Response]
  ): Marshaller[A, Response] = {
    val marshallers = Seq(`application/ld+json`, `application/json`).map(fMarshaller)
    Marshaller.oneOf(marshallers: _*)
  }
} 
Example 60
Source File: AuthDirectives.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.storage.routes

import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.model.headers.OAuth2BearerToken
import akka.http.scaladsl.server.Directive1
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.directives.FutureDirectives.onComplete
import ch.epfl.bluebrain.nexus.storage.IamIdentitiesClient
import ch.epfl.bluebrain.nexus.storage.IamIdentitiesClient.{AccessToken, Caller}
import ch.epfl.bluebrain.nexus.storage.IamIdentitiesClientError.IdentitiesClientStatusError
import ch.epfl.bluebrain.nexus.storage.StorageError._
import com.typesafe.scalalogging.Logger
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global

import scala.util.{Failure, Success}

object AuthDirectives {

  private val logger = Logger[this.type]

  
  def extractCaller(implicit identities: IamIdentitiesClient[Task], token: Option[AccessToken]): Directive1[Caller] =
    onComplete(identities().runToFuture).flatMap {
      case Success(caller)                                                   => provide(caller)
      case Failure(IdentitiesClientStatusError(StatusCodes.Unauthorized, _)) => failWith(AuthenticationFailed)
      case Failure(IdentitiesClientStatusError(StatusCodes.Forbidden, _))    => failWith(AuthorizationFailed)
      case Failure(err)                                                      =>
        val message = "Error when trying to extract the subject"
        logger.error(message, err)
        failWith(InternalError(message))
    }
} 
Example 61
Source File: Routes.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.storage.routes

import akka.http.scaladsl.model.headers.{`WWW-Authenticate`, HttpChallenges}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.{ExceptionHandler, RejectionHandler, Route}
import ch.epfl.bluebrain.nexus.storage.IamIdentitiesClient.Caller
import ch.epfl.bluebrain.nexus.storage.StorageError._
import ch.epfl.bluebrain.nexus.storage.config.AppConfig
import ch.epfl.bluebrain.nexus.storage.config.AppConfig._
import ch.epfl.bluebrain.nexus.storage.routes.AuthDirectives._
import ch.epfl.bluebrain.nexus.storage.routes.PrefixDirectives._
import ch.epfl.bluebrain.nexus.storage.routes.instances._
import ch.epfl.bluebrain.nexus.storage.{AkkaSource, IamIdentitiesClient, Rejection, StorageError, Storages}
import com.typesafe.scalalogging.Logger
import monix.eval.Task

import scala.util.control.NonFatal

object Routes {

  private[this] val logger = Logger[this.type]

  
  def apply(
      storages: Storages[Task, AkkaSource]
  )(implicit config: AppConfig, identities: IamIdentitiesClient[Task]): Route =
    //TODO: Fetch Bearer token and verify identity
    wrap {
      concat(
        AppInfoRoutes(config.description).routes,
        (pathPrefix(config.http.prefix) & extractToken) { implicit token =>
          extractCaller.apply {
            case Caller(config.subject.subjectValue, _) => StorageRoutes(storages).routes
            case _                                      => failWith(AuthenticationFailed)
          }
        }
      )
    }

} 
Example 62
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 63
Source File: AppInfoRoutesSpec.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.storage.routes

import java.util.regex.Pattern.quote

import akka.http.scaladsl.model.StatusCodes._
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.testkit.ScalatestRouteTest
import ch.epfl.bluebrain.nexus.storage.config.{AppConfig, Settings}
import ch.epfl.bluebrain.nexus.storage.routes.instances._
import ch.epfl.bluebrain.nexus.storage.utils.Resources
import ch.epfl.bluebrain.nexus.storage.{AkkaSource, IamIdentitiesClient, Storages}
import io.circe.Json
import monix.eval.Task
import org.mockito.IdiomaticMockito
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike

class AppInfoRoutesSpec
    extends AnyWordSpecLike
    with Matchers
    with ScalatestRouteTest
    with IdiomaticMockito
    with Resources {

  "the app info routes" should {

    implicit val config: AppConfig                        = Settings(system).appConfig
    implicit val iamIdentities: IamIdentitiesClient[Task] = mock[IamIdentitiesClient[Task]]
    val route: Route                                      = Routes(mock[Storages[Task, AkkaSource]])

    "return application information" in {
      Get("/") ~> route ~> check {
        status shouldEqual OK
        responseAs[Json] shouldEqual
          jsonContentOf("/app-info.json", Map(quote("{version}") -> config.description.version))
      }
    }
  }
} 
Example 64
Source File: UserAccountRepositoryOnMemory.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.example.repository.memory

import com.github.j5ik2o.dddbase.example.dao.memory.UserAccountComponent
import com.github.j5ik2o.dddbase.example.model._
import com.github.j5ik2o.dddbase.example.repository.{ OnMemory, UserAccountRepository }
import com.github.j5ik2o.dddbase.memory._
import com.google.common.base.Ticker
import monix.eval.Task

import scala.concurrent.duration.Duration

class UserAccountRepositoryOnMemory(
    concurrencyLevel: Option[Int] = None,
    expireAfterAccess: Option[Duration] = None,
    expireAfterWrite: Option[Duration] = None,
    initialCapacity: Option[Int] = None,
    maximumSize: Option[Int] = None,
    maximumWeight: Option[Int] = None,
    recordStats: Option[Boolean] = None,
    refreshAfterWrite: Option[Duration] = None,
    softValues: Option[Boolean] = None,
    ticker: Option[Ticker] = None,
    weakKeys: Option[Boolean] = None,
    weakValues: Option[Boolean] = None
) extends UserAccountRepository[OnMemory]
    with AggregateSingleReadFeature
    with AggregateSingleWriteFeature
    with AggregateMultiWriteFeature
    with AggregateMultiReadFeature
    with AggregateSingleSoftDeleteFeature
    with AggregateMultiSoftDeleteFeature
    with UserAccountComponent {

  override type RecordType = UserAccountRecord
  override type DaoType    = UserAccountDao

  override protected val dao: UserAccountDao =
    new UserAccountDao(
      concurrencyLevel = concurrencyLevel,
      expireAfterAccess = expireAfterAccess,
      expireAfterWrite = expireAfterWrite,
      initialCapacity = initialCapacity,
      maximumSize = maximumSize,
      maximumWeight = maximumWeight,
      recordStats = recordStats,
      refreshAfterWrite = refreshAfterWrite,
      softValues = softValues,
      ticker = ticker,
      weakKeys = weakKeys,
      weakValues = weakValues
    )

  override protected def convertToAggregate: UserAccountRecord => Task[UserAccount] = { record =>
    Task.pure {
      UserAccount(
        id = UserAccountId(record.id.toLong),
        status = Status.withName(record.status),
        emailAddress = EmailAddress(record.email),
        password = HashedPassword(record.password),
        firstName = record.firstName,
        lastName = record.lastName,
        createdAt = record.createdAt,
        updatedAt = record.updatedAt
      )
    }
  }

  override protected def convertToRecord: UserAccount => Task[UserAccountRecord] = { aggregate =>
    Task.pure {
      UserAccountRecord(
        id = aggregate.id.value.toString,
        status = aggregate.status.entryName,
        email = aggregate.emailAddress.value,
        password = aggregate.password.value,
        firstName = aggregate.firstName,
        lastName = aggregate.lastName,
        createdAt = aggregate.createdAt,
        updatedAt = aggregate.updatedAt
      )
    }
  }

} 
Example 65
Source File: AbstractUserMessageRepositoryBySlick.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.example.repository.slick
import com.github.j5ik2o.dddbase.example.dao.slick.UserMessageComponent
import com.github.j5ik2o.dddbase.example.model.{ Status, UserMessage, UserMessageId }
import com.github.j5ik2o.dddbase.example.repository.{ BySlick, UserMessageRepository }
import com.github.j5ik2o.dddbase.slick._
import monix.eval.Task
import slick.jdbc.JdbcProfile
import slick.lifted.Rep

abstract class AbstractUserMessageRepositoryBySlick(val profile: JdbcProfile, val db: JdbcProfile#Backend#Database)
    extends UserMessageRepository[BySlick]
    with AggregateSingleReadFeature
    with AggregateMultiReadFeature
    with AggregateSingleWriteFeature
    with AggregateMultiWriteFeature
    with UserMessageComponent {

  override type RecordType = UserMessageRecord
  override type TableType  = UserMessages
  override protected val dao = UserMessageDao

  override protected def byCondition(id: IdType): TableType => Rep[Boolean] = { v =>
    import profile.api._
    v.userId === id.userId && v.messageId === id.messageId
  }

  override protected def byConditions(ids: Seq[IdType]): TableType => Rep[Boolean] = { v =>
    import profile.api._
    ids
      .map { id =>
        v.userId === id.userId && v.messageId === id.messageId
      }
      .reduceLeft(_ || _)
  }

  override protected def convertToAggregate: UserMessageRecord => Task[UserMessage] = { record =>
    Task.pure {
      UserMessage(
        id = UserMessageId(record.userId, record.messageId),
        status = Status.withName(record.status),
        message = record.message,
        createdAt = record.createdAt,
        updatedAt = record.updatedAt
      )
    }
  }

  override protected def convertToRecord: UserMessage => Task[UserMessageRecord] = { aggregate =>
    Task.pure {
      UserMessageRecord(
        messageId = aggregate.id.messageId,
        userId = aggregate.id.userId,
        status = aggregate.status.entryName,
        message = aggregate.message,
        createdAt = aggregate.createdAt,
        updatedAt = aggregate.updatedAt
      )
    }
  }

} 
Example 66
Source File: AbstractUserAccountRepositoryBySlick.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.example.repository.slick

import com.github.j5ik2o.dddbase.example.dao.slick.UserAccountComponent
import com.github.j5ik2o.dddbase.example.model._
import com.github.j5ik2o.dddbase.example.repository.{ BySlick, UserAccountRepository }
import com.github.j5ik2o.dddbase.slick.{
  AggregateMultiReadFeature,
  AggregateMultiWriteFeature,
  AggregateSingleReadFeature,
  AggregateSingleWriteFeature
}
import monix.eval.Task
import slick.jdbc.JdbcProfile
import slick.lifted.Rep

abstract class AbstractUserAccountRepositoryBySlick(val profile: JdbcProfile, val db: JdbcProfile#Backend#Database)
    extends UserAccountRepository[BySlick]
    with AggregateSingleReadFeature
    with AggregateMultiReadFeature
    with AggregateSingleWriteFeature
    with AggregateMultiWriteFeature
    with UserAccountComponent {
  override type RecordType = UserAccountRecord
  override type TableType  = UserAccounts
  override protected val dao = UserAccountDao

  override protected def byCondition(id: IdType): TableType => Rep[Boolean] = {
    import profile.api._
    _.id === id.value
  }

  override protected def byConditions(ids: Seq[IdType]): TableType => Rep[Boolean] = {
    import profile.api._
    _.id.inSet(ids.map(_.value))
  }

  override protected def convertToAggregate: UserAccountRecord => Task[UserAccount] = { record =>
    Task.pure {
      UserAccount(
        id = UserAccountId(record.id),
        status = Status.withName(record.status),
        emailAddress = EmailAddress(record.email),
        password = HashedPassword(record.password),
        firstName = record.firstName,
        lastName = record.lastName,
        createdAt = record.createdAt,
        updatedAt = record.updatedAt
      )
    }
  }

  override protected def convertToRecord: UserAccount => Task[UserAccountRecord] = { aggregate =>
    Task.pure {
      UserAccountRecord(
        id = aggregate.id.value,
        status = aggregate.status.entryName,
        email = aggregate.emailAddress.value,
        password = aggregate.password.value,
        firstName = aggregate.firstName,
        lastName = aggregate.lastName,
        createdAt = aggregate.createdAt,
        updatedAt = aggregate.updatedAt
      )
    }
  }

} 
Example 67
Source File: UserAccountRepositoryOnMemcached.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.example.repository.memcached

import akka.actor.ActorSystem
import cats.data.ReaderT
import com.github.j5ik2o.dddbase.example.dao.memcached.UserAccountComponent
import com.github.j5ik2o.dddbase.example.model._
import com.github.j5ik2o.dddbase.example.repository.{ OnMemcached, UserAccountRepository }
import com.github.j5ik2o.dddbase.memcached._
import com.github.j5ik2o.reactive.memcached.MemcachedConnection
import monix.eval.Task

import scala.concurrent.duration._

class UserAccountRepositoryOnMemcached(val expireDuration: Duration)(implicit system: ActorSystem)
    extends UserAccountRepository[OnMemcached]
    with AggregateSingleReadFeature
    with AggregateSingleWriteFeature
    with AggregateMultiReadFeature
    with AggregateMultiWriteFeature
    with AggregateSingleSoftDeleteFeature
    with AggregateMultiSoftDeleteFeature
    with UserAccountComponent {

  require(expireDuration.gteq(1 seconds))

  override type RecordType = UserAccountRecord
  override type DaoType    = UserAccountDao

  override protected val dao: UserAccountDao = UserAccountDao()

  override protected def convertToAggregate: UserAccountRecord => ReaderT[Task, MemcachedConnection, UserAccount] = {
    record =>
      ReaderT { _ =>
        Task.pure {
          UserAccount(
            id = UserAccountId(record.id.toLong),
            status = Status.withName(record.status),
            emailAddress = EmailAddress(record.email),
            password = HashedPassword(record.password),
            firstName = record.firstName,
            lastName = record.lastName,
            createdAt = record.createdAt,
            updatedAt = record.updatedAt
          )
        }
      }
  }

  override protected def convertToRecord: UserAccount => ReaderT[Task, MemcachedConnection, UserAccountRecord] = {
    aggregate =>
      ReaderT { _ =>
        Task.pure {
          UserAccountRecord(
            id = aggregate.id.value.toString,
            status = aggregate.status.entryName,
            email = aggregate.emailAddress.value,
            password = aggregate.password.value,
            firstName = aggregate.firstName,
            lastName = aggregate.lastName,
            createdAt = aggregate.createdAt,
            updatedAt = aggregate.updatedAt
          )
        }
      }
  }

} 
Example 68
Source File: UserAccountRepositoryOnRedis.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.example.repository.redis

import akka.actor.ActorSystem
import cats.data.ReaderT
import com.github.j5ik2o.dddbase.example.dao.redis.UserAccountComponent
import com.github.j5ik2o.dddbase.example.model._
import com.github.j5ik2o.dddbase.example.repository.{ OnRedis, UserAccountRepository }
import com.github.j5ik2o.dddbase.redis._
import com.github.j5ik2o.reactive.redis.RedisConnection
import monix.eval.Task

import scala.concurrent.duration.Duration

class UserAccountRepositoryOnRedis(val expireDuration: Duration)(implicit system: ActorSystem)
    extends UserAccountRepository[OnRedis]
    with AggregateSingleReadFeature
    with AggregateSingleWriteFeature
    with AggregateMultiReadFeature
    with AggregateMultiWriteFeature
    with AggregateSingleSoftDeleteFeature
    with AggregateMultiSoftDeleteFeature
    with UserAccountComponent {

  override type RecordType = UserAccountRecord
  override type DaoType    = UserAccountDao

  override protected val dao = UserAccountDao()

  override protected def convertToAggregate: UserAccountRecord => ReaderT[Task, RedisConnection, UserAccount] = {
    record =>
      ReaderT { _ =>
        Task.pure {
          UserAccount(
            id = UserAccountId(record.id.toLong),
            status = Status.withName(record.status),
            emailAddress = EmailAddress(record.email),
            password = HashedPassword(record.password),
            firstName = record.firstName,
            lastName = record.lastName,
            createdAt = record.createdAt,
            updatedAt = record.updatedAt
          )
        }
      }
  }

  override protected def convertToRecord: UserAccount => ReaderT[Task, RedisConnection, UserAccountRecord] = {
    aggregate =>
      ReaderT { _ =>
        Task.pure {
          UserAccountRecord(
            id = aggregate.id.value.toString,
            status = aggregate.status.entryName,
            email = aggregate.emailAddress.value,
            password = aggregate.password.value,
            firstName = aggregate.firstName,
            lastName = aggregate.lastName,
            createdAt = aggregate.createdAt,
            updatedAt = aggregate.updatedAt
          )
        }
      }
  }

} 
Example 69
Source File: UserAccountRepositoryBySkinny.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.example.repository.skinny

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.example.dao.skinny.UserAccountComponent
import com.github.j5ik2o.dddbase.example.model._
import com.github.j5ik2o.dddbase.example.repository.{ BySkinny, UserAccountRepository }
import com.github.j5ik2o.dddbase.skinny._
import monix.eval.Task
import scalikejdbc._

trait UserAccountRepositoryBySkinny
    extends UserAccountRepository[BySkinny]
    with AggregateSingleReadFeature
    with AggregateSingleWriteFeature
    with AggregateMultiReadFeature
    with AggregateMultiWriteFeature
    with UserAccountComponent {

  override type RecordIdType = Long
  override type RecordType   = UserAccountRecord
  override type DaoType      = UserAccountDao.type
  override protected val dao: UserAccountDao.type = UserAccountDao

  override protected def toRecordId(id: UserAccountId): Long = id.value

  override protected def byCondition(id: IdType): SQLSyntax        = sqls.eq(dao.defaultAlias.id, id.value)
  override protected def byConditions(ids: Seq[IdType]): SQLSyntax = sqls.in(dao.defaultAlias.id, ids.map(_.value))

  override protected def convertToAggregate: UserAccountRecord => ReaderT[Task, DBSession, UserAccount] = { record =>
    ReaderT { _ =>
      Task.pure {
        UserAccount(
          id = UserAccountId(record.id),
          status = Status.withName(record.status),
          emailAddress = EmailAddress(record.email),
          password = HashedPassword(record.password),
          firstName = record.firstName,
          lastName = record.lastName,
          createdAt = record.createdAt,
          updatedAt = record.updatedAt
        )
      }
    }
  }

  override protected def convertToRecord: UserAccount => ReaderT[Task, DBSession, UserAccountRecord] = { aggregate =>
    ReaderT { _ =>
      Task.pure {
        UserAccountRecord(
          id = aggregate.id.value,
          status = aggregate.status.entryName,
          email = aggregate.emailAddress.value,
          password = aggregate.password.value,
          firstName = aggregate.firstName,
          lastName = aggregate.lastName,
          createdAt = aggregate.createdAt,
          updatedAt = aggregate.updatedAt
        )
      }
    }
  }
}

class UserAccountRepositoryBySkinnyImpl
    extends UserAccountRepositoryBySkinny
    with AggregateSingleSoftDeleteFeature
    with AggregateMultiSoftDeleteFeature 
Example 70
Source File: UserMessageRepositoryBySkinny.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.example.repository.skinny

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.example.dao.skinny.UserMessageComponent
import com.github.j5ik2o.dddbase.example.model._
import com.github.j5ik2o.dddbase.example.repository.{ BySkinny, UserMessageRepository }
import com.github.j5ik2o.dddbase.skinny._
import monix.eval.Task
import scalikejdbc.{ sqls, DBSession, SQLSyntax }

trait UserMessageRepositoryBySkinny
    extends UserMessageRepository[BySkinny]
    with AggregateSingleReadFeature
    with AggregateSingleWriteFeature
    with AggregateMultiReadFeature
    with AggregateMultiWriteFeature
    with UserMessageComponent {
  override type RecordIdType = UserMessageRecordId
  override type RecordType   = UserMessageRecord
  override type DaoType      = UserMessageDao.type
  override protected val dao: UserMessageDao.type = UserMessageDao

  override protected def toRecordId(id: UserMessageId): UserMessageRecordId =
    UserMessageRecordId(id.messageId, id.userId)

  override protected def byCondition(id: IdType): SQLSyntax =
    sqls.eq(dao.column.messageId, id.messageId).and.eq(dao.column.userId, id.userId)

  override protected def byConditions(ids: Seq[IdType]): SQLSyntax =
    sqls.in((dao.column.messageId, dao.column.userId), ids.map(v => (v.messageId, v.userId)))

  override protected def convertToAggregate: UserMessageRecord => ReaderT[Task, DBSession, UserMessage] = { record =>
    ReaderT { _ =>
      Task.pure {
        UserMessage(
          id = UserMessageId(record.userId, record.messageId),
          status = Status.withName(record.status),
          message = record.message,
          createdAt = record.createdAt,
          updatedAt = record.updatedAt
        )
      }
    }
  }

  override protected def convertToRecord: UserMessage => ReaderT[Task, DBSession, UserMessageRecord] = { aggregate =>
    ReaderT { _ =>
      Task.pure {
        UserMessageRecord(
          messageId = aggregate.id.messageId,
          userId = aggregate.id.userId,
          status = aggregate.status.entryName,
          message = aggregate.message,
          createdAt = aggregate.createdAt,
          updatedAt = aggregate.updatedAt
        )
      }
    }
  }

}

class UserMessageRepositoryBySkinnyImpl
    extends UserMessageRepositoryBySkinny
    with AggregateSingleSoftDeleteFeature
    with AggregateMultiSoftDeleteFeature 
Example 71
Source File: UserAccountRepositoryOnDynamoDB.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.example.repository.dynamodb

import com.github.j5ik2o.dddbase.dynamodb._
import com.github.j5ik2o.dddbase.example.dao.dynamodb.UserAccountComponent
import com.github.j5ik2o.dddbase.example.model._
import com.github.j5ik2o.dddbase.example.repository.{ OnDynamoDB, UserAccountRepository }
import com.github.j5ik2o.reactive.aws.dynamodb.monix.DynamoDbMonixClient
import monix.eval.Task

class UserAccountRepositoryOnDynamoDB(client: DynamoDbMonixClient)
    extends UserAccountRepository[OnDynamoDB]
    with AggregateSingleReadFeature
    with AggregateSingleWriteFeature
    with AggregateMultiReadFeature
    with AggregateMultiWriteFeature
    with AggregateSingleSoftDeleteFeature
    with AggregateMultiSoftDeleteFeature
    with UserAccountComponent {
  override type RecordIdType = String
  override type RecordType   = UserAccountRecord
  override type DaoType      = UserAccountDao
  override protected val dao = UserAccountDao(client)

  override protected def toRecordId(id: UserAccountId): String = id.value.toString

  override protected def convertToAggregate: UserAccountRecord => Task[UserAccount] = { record =>
    Task.pure {
      UserAccount(
        id = UserAccountId(record.id.toLong),
        status = Status.withName(record.status),
        emailAddress = EmailAddress(record.email),
        password = HashedPassword(record.password),
        firstName = record.firstName,
        lastName = record.lastName,
        createdAt = record.createdAt,
        updatedAt = record.updatedAt
      )
    }
  }

  override protected def convertToRecord: UserAccount => Task[UserAccountRecord] = { aggregate =>
    Task.pure {
      UserAccountRecord(
        id = aggregate.id.value.toString,
        status = aggregate.status.entryName,
        email = aggregate.emailAddress.value,
        password = aggregate.password.value,
        firstName = aggregate.firstName,
        lastName = aggregate.lastName,
        createdAt = aggregate.createdAt,
        updatedAt = aggregate.updatedAt
      )
    }
  }
} 
Example 72
Source File: UserMessageRepositoryOnDynamoDB.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.example.repository.dynamodb

import com.github.j5ik2o.dddbase.dynamodb._
import com.github.j5ik2o.dddbase.example.dao.dynamodb.UserMessageComponent
import com.github.j5ik2o.dddbase.example.model._
import com.github.j5ik2o.dddbase.example.repository.{ OnDynamoDB, UserMessageRepository }
import com.github.j5ik2o.reactive.aws.dynamodb.monix.DynamoDbMonixClient
import monix.eval.Task

class UserMessageRepositoryOnDynamoDB(client: DynamoDbMonixClient)
    extends UserMessageRepository[OnDynamoDB]
    with AggregateSingleReadFeature
    with AggregateSingleWriteFeature
    with AggregateMultiReadFeature
    with AggregateMultiWriteFeature
    with AggregateSingleSoftDeleteFeature
    with AggregateMultiSoftDeleteFeature
    with UserMessageComponent {
  override type RecordIdType = UserMessageRecordId
  override type RecordType   = UserMessageRecord
  override type DaoType      = UserMessageDao
  override protected val dao = UserMessageDao(client)

  override protected def toRecordId(
      id: UserMessageId
  ): UserMessageRecordId = UserMessageRecordId(id.userId, id.messageId)

  override protected def convertToAggregate: UserMessageRecord => Task[UserMessage] = { record =>
    Task.pure {
      UserMessage(
        id = UserMessageId(record.id.userId, record.id.messageId),
        status = Status.withName(record.status),
        message = record.message,
        createdAt = record.createdAt,
        updatedAt = record.updatedAt
      )
    }
  }

  override protected def convertToRecord: UserMessage => Task[UserMessageRecord] = { aggregate =>
    Task.pure {
      UserMessageRecord(
        id = UserMessageRecordId(aggregate.id.userId, aggregate.id.messageId),
        status = aggregate.status.entryName,
        message = aggregate.message,
        createdAt = aggregate.createdAt,
        updatedAt = aggregate.updatedAt
      )
    }
  }

} 
Example 73
Source File: UserAccountRepositoryOnMemorySpec.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.example.repository.memory

import java.time.ZonedDateTime

import com.github.j5ik2o.dddbase.AggregateNotFoundException
import com.github.j5ik2o.dddbase.example.model._
import com.github.j5ik2o.dddbase.example.repository.{ IdGenerator, SpecSupport, UserAccountRepository }
import com.github.j5ik2o.dddbase.example.repository.util.ScalaFuturesSupportSpec
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.{ FreeSpec, Matchers }

import scala.concurrent.duration._
import scala.concurrent.{ Await, Future }

class UserAccountRepositoryOnMemorySpec
    extends FreeSpec
    with ScalaFutures
    with ScalaFuturesSupportSpec
    with Matchers
    with SpecSupport {

  val userAccount = UserAccount(
    id = UserAccountId(IdGenerator.generateIdValue),
    status = Status.Active,
    emailAddress = EmailAddress("[email protected]"),
    password = HashedPassword("aaa"),
    firstName = "Junichi",
    lastName = "Kato",
    createdAt = ZonedDateTime.now,
    updatedAt = None
  )

  val userAccounts = for (idx <- 1L to 10L)
    yield
      UserAccount(
        id = UserAccountId(IdGenerator.generateIdValue),
        status = Status.Active,
        emailAddress = EmailAddress(s"user${idx}@gmail.com"),
        password = HashedPassword("aaa"),
        firstName = "Junichi",
        lastName = "Kato",
        createdAt = ZonedDateTime.now,
        updatedAt = None
      )

  "UserAccountRepositoryOnMemory" - {
    "store" in {
      val repository = UserAccountRepository.onMemory()
      val result: UserAccount = (for {
        _ <- repository.store(userAccount)
        r <- repository.resolveById(userAccount.id)
      } yield r).runToFuture.futureValue

      result shouldBe userAccount
    }
    "storeMulti" in {
      val repository = UserAccountRepository.onMemory()
      val result: Seq[UserAccount] = (for {
        _ <- repository.storeMulti(userAccounts)

        r <- repository.resolveMulti(userAccounts.map(_.id))
      } yield r).runToFuture.futureValue

      sameAs(result, userAccounts) shouldBe true
    }
    "store then expired" in {
      val repository = UserAccountRepository.onMemory(expireAfterWrite = Some(1 seconds))
      val resultFuture: Future[UserAccount] = (for {
        _ <- repository.store(userAccount)
        _ <- Task.pure(Thread.sleep(1000))
        r <- repository.resolveById(userAccount.id)
      } yield r).runToFuture

      an[AggregateNotFoundException] should be thrownBy {
        Await.result(resultFuture, Duration.Inf)
      }
    }
  }

} 
Example 74
Source File: AggregateSingleReadFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.memory

import com.github.j5ik2o.dddbase.{ AggregateNotFoundException, AggregateSingleReader }
import monix.eval.Task

trait AggregateSingleReadFeature extends AggregateSingleReader[Task] with AggregateBaseReadFeature {

  override def resolveById(id: IdType): Task[AggregateType] =
    for {
      record <- dao.get(id.value.toString).flatMap {
        case Some(v) =>
          Task.pure(v)
        case None =>
          Task.raiseError(AggregateNotFoundException(id))
      }
      aggregate <- convertToAggregate(record)
    } yield aggregate

} 
Example 75
Source File: AggregateSingleReadFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.memcached

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.{ AggregateNotFoundException, AggregateSingleReader }
import com.github.j5ik2o.reactive.memcached.MemcachedConnection
import monix.eval.Task

trait AggregateSingleReadFeature
    extends AggregateSingleReader[ReaderT[Task, MemcachedConnection, ?]]
    with AggregateBaseReadFeature {

  override def resolveById(id: IdType): ReaderT[Task, MemcachedConnection, AggregateType] =
    for {
      record <- ReaderT[Task, MemcachedConnection, RecordType] { con =>
        dao.get(id.value.toString).run(con).flatMap {
          case Some(v) => Task.pure(v)
          case None    => Task.raiseError(AggregateNotFoundException(id))
        }
      }
      aggregate <- convertToAggregate(record)
    } yield aggregate

} 
Example 76
Source File: AggregateSingleSoftDeleteFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.memcached

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.AggregateSingleSoftDeletable
import com.github.j5ik2o.reactive.memcached.MemcachedConnection
import monix.eval.Task

trait AggregateSingleSoftDeleteFeature
    extends AggregateSingleSoftDeletable[ReaderT[Task, MemcachedConnection, ?]]
    with AggregateBaseReadFeature {
  override type RecordType <: MemcachedDaoSupport#SoftDeletableRecord
  override type DaoType <: MemcachedDaoSupport#Dao[ReaderT[Task, MemcachedConnection, ?], RecordType] with MemcachedDaoSupport#DaoSoftDeletable[
    ReaderT[Task, MemcachedConnection, ?],
    RecordType
  ]

  override def softDelete(id: IdType): ReaderT[Task, MemcachedConnection, Long] = ReaderT { con =>
    dao.softDelete(id.value.toString).run(con)
  }

} 
Example 77
Source File: AggregateSingleWriteFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.memcached

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.AggregateSingleWriter
import com.github.j5ik2o.reactive.memcached.MemcachedConnection
import monix.eval.Task

trait AggregateSingleWriteFeature
    extends AggregateSingleWriter[ReaderT[Task, MemcachedConnection, ?]]
    with AggregateBaseWriteFeature {

  override def store(aggregate: AggregateType): ReaderT[Task, MemcachedConnection, Long] = {
    for {
      record <- convertToRecord(aggregate)
      result <- dao.set(record, expireDuration)
    } yield result
  }

} 
Example 78
Source File: AggregateMultiReadFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.memcached

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.AggregateMultiReader
import com.github.j5ik2o.reactive.memcached.MemcachedConnection
import monix.eval.Task

trait AggregateMultiReadFeature
    extends AggregateMultiReader[ReaderT[Task, MemcachedConnection, ?]]
    with AggregateBaseReadFeature {

  override def resolveMulti(ids: Seq[IdType]): ReaderT[Task, MemcachedConnection, Seq[AggregateType]] =
    ReaderT[Task, MemcachedConnection, Seq[AggregateType]] { con =>
      for {
        results    <- dao.getMulti(ids.map(_.value.toString)).run(con)
        aggregates <- Task.gather(results.map(v => convertToAggregate(v)(con)))
      } yield aggregates
    }

} 
Example 79
Source File: AggregateMultiWriteFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.memcached

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.AggregateMultiWriter
import com.github.j5ik2o.reactive.memcached.MemcachedConnection
import monix.eval.Task

trait AggregateMultiWriteFeature
    extends AggregateMultiWriter[ReaderT[Task, MemcachedConnection, ?]]
    with AggregateBaseWriteFeature {

  override def storeMulti(aggregates: Seq[AggregateType]): ReaderT[Task, MemcachedConnection, Long] =
    ReaderT[Task, MemcachedConnection, Long] { con =>
      for {
        records <- Task.traverse(aggregates) { aggregate =>
          convertToRecord(aggregate)(con)
        }
        result <- dao.setMulti(records, expireDuration).run(con)
      } yield result
    }

} 
Example 80
Source File: AggregateSingleReadFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.redis

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.{ AggregateNotFoundException, AggregateSingleReader }
import com.github.j5ik2o.reactive.redis.RedisConnection
import monix.eval.Task

trait AggregateSingleReadFeature
    extends AggregateSingleReader[ReaderT[Task, RedisConnection, ?]]
    with AggregateBaseReadFeature {

  override def resolveById(id: IdType): ReaderT[Task, RedisConnection, AggregateType] =
    for {
      record <- ReaderT[Task, RedisConnection, RecordType] { con =>
        dao.get(id.value.toString).run(con).flatMap {
          case Some(v) =>
            Task.pure(v)
          case None =>
            Task.raiseError(AggregateNotFoundException(id))
        }
      }
      aggregate <- convertToAggregate(record)
    } yield aggregate

} 
Example 81
Source File: AggregateSingleSoftDeleteFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.redis

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.AggregateSingleSoftDeletable
import com.github.j5ik2o.reactive.redis.RedisConnection
import monix.eval.Task

trait AggregateSingleSoftDeleteFeature
    extends AggregateSingleSoftDeletable[ReaderT[Task, RedisConnection, ?]]
    with AggregateBaseReadFeature {
  override type RecordType <: RedisDaoSupport#SoftDeletableRecord
  override type DaoType <: RedisDaoSupport#Dao[ReaderT[Task, RedisConnection, ?], RecordType] with RedisDaoSupport#DaoSoftDeletable[
    ReaderT[Task, RedisConnection, ?],
    RecordType
  ]

  override def softDelete(id: IdType): ReaderT[Task, RedisConnection, Long] = ReaderT { con =>
    dao.softDelete(id.value.toString).run(con)
  }

} 
Example 82
Source File: AggregateSingleWriteFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.redis

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.AggregateSingleWriter
import com.github.j5ik2o.reactive.redis.RedisConnection
import monix.eval.Task

trait AggregateSingleWriteFeature
    extends AggregateSingleWriter[ReaderT[Task, RedisConnection, ?]]
    with AggregateBaseWriteFeature {

  override def store(aggregate: AggregateType): ReaderT[Task, RedisConnection, Long] = {
    for {
      record <- convertToRecord(aggregate)
      result <- dao.set(record, expireDuration)
    } yield result
  }

} 
Example 83
Source File: AggregateMultiReadFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.redis

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.AggregateMultiReader
import com.github.j5ik2o.reactive.redis.RedisConnection
import monix.eval.Task

trait AggregateMultiReadFeature
    extends AggregateMultiReader[ReaderT[Task, RedisConnection, ?]]
    with AggregateBaseReadFeature {

  override def resolveMulti(ids: Seq[IdType]): ReaderT[Task, RedisConnection, Seq[AggregateType]] =
    ReaderT[Task, RedisConnection, Seq[AggregateType]] { con =>
      for {
        results    <- dao.getMulti(ids.map(_.value.toString)).run(con)
        aggregates <- Task.gather(results.map(convertToAggregate(_)(con)))
      } yield aggregates
    }

} 
Example 84
Source File: AggregateMultiWriteFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.redis

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.AggregateMultiWriter
import com.github.j5ik2o.reactive.redis.RedisConnection
import monix.eval.Task

trait AggregateMultiWriteFeature
    extends AggregateMultiWriter[ReaderT[Task, RedisConnection, ?]]
    with AggregateBaseWriteFeature {

  override def storeMulti(aggregates: Seq[AggregateType]): ReaderT[Task, RedisConnection, Long] =
    ReaderT[Task, RedisConnection, Long] { con =>
      for {
        records <- Task.traverse(aggregates) { aggregate =>
          convertToRecord(aggregate)(con)
        }
        result <- dao.setMulti(records, expireDuration).run(con)
      } yield result
    }

} 
Example 85
Source File: AggregateSingleReadFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.dynamodb

import com.github.j5ik2o.dddbase.{ AggregateNotFoundException, AggregateSingleReader }
import monix.eval.Task

trait AggregateSingleReadFeature extends AggregateSingleReader[Task] with AggregateBaseReadFeature {

  override def resolveById(id: IdType): Task[AggregateType] = {
    for {
      record <- dao.get(toRecordId(id)).flatMap {
        case Some(v) => Task.pure(v)
        case None    => Task.raiseError(AggregateNotFoundException(id))
      }
      aggregate <- convertToAggregate(record)
    } yield aggregate
  }

} 
Example 86
Source File: AggregateSingleReadFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.slick

import com.github.j5ik2o.dddbase.{ AggregateNotFoundException, AggregateSingleReader }
import monix.eval.Task

trait AggregateSingleReadFeature extends AggregateSingleReader[Task] with AggregateBaseReadFeature {

  override def resolveById(id: IdType): Task[AggregateType] =
    for {
      record <- Task
        .deferFutureAction { implicit ec =>
          import profile.api._
          db.run(dao.filter(byCondition(id)).take(1).result)
            .map(_.headOption)
            .map(_.getOrElse(throw AggregateNotFoundException(id)))
        }
      aggregate <- convertToAggregate(record)
    } yield aggregate
} 
Example 87
Source File: AggregateSingleSoftDeleteFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.slick

import com.github.j5ik2o.dddbase.AggregateSingleSoftDeletable
import monix.eval.Task
import slick.lifted.{ Rep, TableQuery }

trait AggregateSingleSoftDeleteFeature extends AggregateSingleSoftDeletable[Task] with AggregateBaseReadFeature {

  override type RecordType <: SlickDaoSupport#SoftDeletableRecord
  override type TableType <: SlickDaoSupport#TableBase[RecordType] with SlickDaoSupport#SoftDeletableTableSupport[
    RecordType
  ]
  protected final val DELETE = "deleted"
  override protected val dao: TableQuery[TableType]

  override def softDelete(id: IdType): Task[Long] =
    Task.deferFutureAction { implicit ec =>
      import profile.api._
      db.run(dao.filter(byCondition(id)).map(_.status).update(DELETE)).map(_.toLong)
    }.asyncBoundary

  abstract override protected def byCondition(id: IdType): TableType => Rep[Boolean] = { e =>
    import profile.api._
    super.byCondition(id)(e) && e.status =!= DELETE
  }

  abstract override protected def byConditions(ids: Seq[IdType]): TableType => Rep[Boolean] = { e =>
    import profile.api._
    super.byConditions(ids)(e) && e.status =!= DELETE
  }

} 
Example 88
Source File: AggregateChunkReadFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.slick

import com.github.j5ik2o.dddbase.{ AggregateChunkReader, AggregatesChunk }
import monix.eval.Task

trait AggregateChunkReadFeature extends AggregateChunkReader[Task] with AggregateBaseReadFeature {

  import profile.api._

  override def resolveMultiWithOffsetLimit(offset: Option[Long], limit: Long): Task[AggregatesChunk[AggregateType]] = {
    val index = offset.map(_.toInt).getOrElse(0)
    for {
      results <- Task.deferFuture {
        db.run(dao.drop(index).take(limit).result)
      }
      aggregates <- Task.traverse(results)(convertToAggregate(_))
    } yield AggregatesChunk(index, aggregates)
  }

} 
Example 89
Source File: AggregateMultiWriteFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.slick

import com.github.j5ik2o.dddbase.AggregateMultiWriter
import monix.eval.Task

trait AggregateMultiWriteFeature extends AggregateMultiWriter[Task] with AggregateBaseWriteFeature {

  override def storeMulti(aggregates: Seq[AggregateType]): Task[Long] =
    for {
      records <- Task.traverse(aggregates) { aggregate =>
        convertToRecord(aggregate)
      }
      result <- Task.deferFutureAction { implicit ec =>
        import profile.api._
        db.run(DBIO.sequence(records.foldLeft(Seq.empty[DBIO[Long]]) {
            case (result, record) =>
              result :+ dao.insertOrUpdate(record).map(_.toLong)
          }))
          .map(_.sum)
      }
    } yield result
} 
Example 90
Source File: AggregateSingleReadFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.skinny

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.{ AggregateNotFoundException, AggregateSingleReader }
import monix.eval.Task
import scalikejdbc.DBSession

trait AggregateSingleReadFeature
    extends AggregateSingleReader[ReaderT[Task, DBSession, ?]]
    with AggregateBaseReadFeature {

  override def resolveById(id: IdType): ReaderT[Task, DBSession, AggregateType] =
    for {
      record <- ReaderT[Task, DBSession, RecordType] { implicit dbSession: DBSession =>
        Task {
          dao.findBy(byCondition(id)).getOrElse(throw AggregateNotFoundException(id))
        }
      }
      aggregate <- convertToAggregate(record)
    } yield aggregate

} 
Example 91
Source File: AggregateSingleSoftDeleteFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.skinny

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.AggregateSingleSoftDeletable
import monix.eval.Task
import scalikejdbc._

trait AggregateSingleSoftDeleteFeature
    extends AggregateSingleSoftDeletable[ReaderT[Task, DBSession, ?]]
    with AggregateBaseReadFeature {

  protected final val DELETE = "deleted"

  override def softDelete(id: IdType): ReaderT[Task, DBSession, Long] = ReaderT { implicit dbSession =>
    Task {
      dao.updateById(toRecordId(id)).withAttributes('status -> DELETE).toLong
    }
  }

  abstract override protected def byCondition(id: IdType): SQLSyntax =
    super.byCondition(id).and.ne(dao.defaultAlias.status, DELETE)

  abstract override protected def byConditions(ids: Seq[IdType]): SQLSyntax =
    super.byConditions(ids).and.ne(dao.defaultAlias.status, DELETE)
} 
Example 92
Source File: AggregateMultiSoftDeleteFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.skinny

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.{ AggregateMultiSoftDeletable, AggregateMultiWriter }
import monix.eval.Task
import scalikejdbc.DBSession

trait AggregateMultiSoftDeleteFeature
    extends AggregateMultiSoftDeletable[ReaderT[Task, DBSession, ?]]
    with AggregateBaseReadFeature {
  this: AggregateMultiWriter[ReaderT[Task, DBSession, ?]] with AggregateSingleSoftDeleteFeature =>

  override def softDeleteMulti(ids: Seq[IdType]): ReaderT[Task, DBSession, Long] = ReaderT { implicit dbDesion =>
    Task {
      dao.updateBy(byConditions(ids)).withAttributes('status -> DELETE).toLong
    }
  }

} 
Example 93
Source File: AggregateSingleWriteFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.skinny

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.AggregateSingleWriter
import monix.eval.Task
import scalikejdbc.DBSession

trait AggregateSingleWriteFeature
    extends AggregateSingleWriter[ReaderT[Task, DBSession, ?]]
    with AggregateBaseWriteFeature {

  override def store(aggregate: AggregateType): ReaderT[Task, DBSession, Long] = {
    for {
      record <- convertToRecord(aggregate)
      result <- ReaderT[Task, DBSession, Long] { implicit dbSession =>
        Task {
          dao.createOrUpdate(record)
        }
      }
    } yield result
  }

} 
Example 94
Source File: AggregateMultiHardDeleteFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.skinny

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.{ AggregateMultiHardDeletable, AggregateMultiWriter }
import monix.eval.Task
import scalikejdbc.DBSession

trait AggregateMultiHardDeleteFeature
    extends AggregateMultiHardDeletable[ReaderT[Task, DBSession, ?]]
    with AggregateBaseReadFeature {
  this: AggregateMultiWriter[ReaderT[Task, DBSession, ?]] with AggregateSingleHardDeleteFeature =>

  override def hardDeleteMulti(ids: Seq[IdType]): ReaderT[Task, DBSession, Long] = ReaderT { implicit dbSession =>
    Task {
      dao.deleteBy(byConditions(ids)).toLong
    }
  }

} 
Example 95
Source File: AggregateChunkReadFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.skinny

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.{ AggregateChunkReader, AggregatesChunk }
import monix.eval.Task
import scalikejdbc.DBSession

trait AggregateChunkReadFeature
    extends AggregateChunkReader[ReaderT[Task, DBSession, ?]]
    with AggregateBaseReadFeature {

  override def resolveMultiWithOffsetLimit(
      offset: Option[Long],
      limit: Long
  ): ReaderT[Task, DBSession, AggregatesChunk[AggregateType]] =
    ReaderT[Task, DBSession, AggregatesChunk[AggregateType]] { implicit dbSession: DBSession =>
      val index = offset.map(_.toInt).getOrElse(0)
      for {
        results <- Task {
          dao.findAllWithLimitOffset(limit.toInt, index)
        }
        aggregates <- Task.gather(results.map(convertToAggregate(_)(dbSession)))
      } yield AggregatesChunk(index, aggregates)
    }

} 
Example 96
Source File: AggregateMultiReadFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.skinny

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.AggregateMultiReader
import monix.eval.Task
import scalikejdbc.DBSession

trait AggregateMultiReadFeature
    extends AggregateMultiReader[ReaderT[Task, DBSession, ?]]
    with AggregateBaseReadFeature {

  override def resolveMulti(ids: Seq[IdType]): ReaderT[Task, DBSession, Seq[AggregateType]] =
    ReaderT[Task, DBSession, Seq[AggregateType]] { implicit dbSession: DBSession =>
      for {
        results <- Task {
          dao.findAllBy(byConditions(ids))
        }
        aggregates <- Task.gather(results.map(convertToAggregate(_)(dbSession)))
      } yield aggregates
    }

} 
Example 97
Source File: AggregateMultiWriteFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.skinny

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.AggregateMultiWriter
import monix.eval.Task
import scalikejdbc.DBSession

trait AggregateMultiWriteFeature
    extends AggregateMultiWriter[ReaderT[Task, DBSession, ?]]
    with AggregateBaseWriteFeature {

  override def storeMulti(aggregates: Seq[AggregateType]): ReaderT[Task, DBSession, Long] =
    ReaderT[Task, DBSession, Long] { dbSession =>
      for {
        records <- Task.traverse(aggregates) { aggregate =>
          convertToRecord(aggregate)(dbSession)
        }
        result <- Task
          .traverse(records) { record =>
            Task { dao.createOrUpdate(record) }
          }
          .map(_.count(_ > 0))
      } yield result
    }

} 
Example 98
Source File: AggregateAllReadFeature.scala    From scala-ddd-base   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.dddbase.skinny

import cats.data.ReaderT
import com.github.j5ik2o.dddbase.AggregateAllReader
import monix.eval.Task
import scalikejdbc.DBSession

trait AggregateAllReadFeature extends AggregateAllReader[ReaderT[Task, DBSession, ?]] with AggregateBaseReadFeature {

  override def resolveAll: ReaderT[Task, DBSession, Seq[AggregateType]] = ReaderT[Task, DBSession, Seq[AggregateType]] {
    implicit dbSession: DBSession =>
      for {
        results <- Task {
          dao.findAll()
        }
        aggregates <- Task.gather(results.map(convertToAggregate(_)(dbSession)))
      } yield aggregates
  }

} 
Example 99
Source File: ConditionalLoggerSpec.scala    From odin   with Apache License 2.0 5 votes vote down vote up
package io.odin.extras.loggers

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

class ConditionalLoggerSpec extends OdinSpec {

  implicit private val scheduler: TestScheduler = TestScheduler()

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

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

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

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

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

          written <- ref.get
        } yield written

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

      written shouldBe expected
    }
  }

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

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

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

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

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

      attempt shouldBe Left(error)
      written shouldBe expected
    }
  }

} 
Example 100
Source File: AsyncLoggerSpec.scala    From odin   with Apache License 2.0 5 votes vote down vote up
package io.odin.loggers

import cats.effect.Resource
import cats.effect.concurrent.Ref
import cats.instances.list._
import cats.syntax.all._
import io.odin.{Level, Logger, LoggerMessage, OdinSpec}
import monix.catnap.ConcurrentQueue
import monix.eval.Task
import monix.execution.schedulers.TestScheduler
import io.odin.syntax._

import scala.concurrent.duration._

class AsyncLoggerSpec extends OdinSpec {
  implicit private val scheduler: TestScheduler = TestScheduler()

  case class RefLogger(ref: Ref[Task, List[LoggerMessage]]) extends DefaultLogger[Task] {
    def log(msg: LoggerMessage): Task[Unit] = Task.raiseError(new IllegalStateException("Async should always batch"))

    override def log(msgs: List[LoggerMessage]): Task[Unit] = {
      ref.update(_ ::: msgs)
    }
  }

  it should "push logs down the chain" in {
    forAll { msgs: List[LoggerMessage] =>
      (for {
        ref <- Resource.liftF(Ref.of[Task, List[LoggerMessage]](List.empty))
        logger <- RefLogger(ref).withMinimalLevel(Level.Trace).withAsync()
        _ <- Resource.liftF(msgs.traverse(logger.log))
        _ = scheduler.tick(10.millis)
        reported <- Resource.liftF(ref.get)
      } yield {
        reported shouldBe msgs
      }).use(Task(_)).runSyncUnsafe()
    }
  }

  it should "push logs to the queue" in {
    forAll { msgs: List[LoggerMessage] =>
      (for {
        queue <- ConcurrentQueue.unbounded[Task, LoggerMessage]()
        logger = AsyncLogger(queue, 1.millis, Logger.noop[Task])
        _ <- msgs.traverse(logger.log)
        reported <- queue.drain(0, Int.MaxValue)
      } yield {
        reported shouldBe msgs
      }).runSyncUnsafe()
    }
  }

  it should "ignore errors in underlying logger" in {
    val errorLogger = new DefaultLogger[Task] {
      def log(msg: LoggerMessage): Task[Unit] = Task.raiseError(new Error)
    }
    forAll { msgs: List[LoggerMessage] =>
      (for {
        queue <- ConcurrentQueue.unbounded[Task, LoggerMessage]()
        logger = AsyncLogger(queue, 1.millis, errorLogger)
        _ <- logger.log(msgs)
        result <- logger.drain
      } yield {
        result shouldBe (())
      }).runSyncUnsafe()
    }
  }
} 
Example 101
Source File: FileLoggerSpec.scala    From odin   with Apache License 2.0 5 votes vote down vote up
package io.odin.loggers

import java.nio.file.{Files, Path, Paths}
import java.util.UUID

import cats.effect.Resource
import io.odin._
import io.odin.formatter.Formatter
import io.odin.{LoggerMessage, OdinSpec}
import scala.concurrent.duration._
import monix.eval.Task
import monix.execution.schedulers.TestScheduler

class FileLoggerSpec extends OdinSpec {
  implicit private val scheduler: TestScheduler = TestScheduler()

  private val fileResource = Resource.make[Task, Path] {
    Task.delay(Files.createTempFile(UUID.randomUUID().toString, ""))
  } { file =>
    Task.delay(Files.delete(file))
  }

  it should "write formatted message into file" in {
    forAll { (loggerMessage: LoggerMessage, formatter: Formatter) =>
      (for {
        path <- fileResource
        fileName = path.toString
        logger <- FileLogger[Task](fileName, formatter, Level.Trace)
        _ <- Resource.liftF(logger.log(loggerMessage))
      } yield {
        new String(Files.readAllBytes(Paths.get(fileName))) shouldBe formatter.format(loggerMessage) + lineSeparator
      }).use(Task(_))
        .runSyncUnsafe()
    }
  }

  it should "write formatted messages into file" in {
    forAll { (loggerMessage: List[LoggerMessage], formatter: Formatter) =>
      (for {
        path <- fileResource
        fileName = path.toString
        logger <- FileLogger[Task](fileName, formatter, Level.Trace)
        _ <- Resource.liftF(logger.log(loggerMessage))
      } yield {
        new String(Files.readAllBytes(Paths.get(fileName))) shouldBe loggerMessage
          .map(formatter.format)
          .mkString(lineSeparator) + (if (loggerMessage.isEmpty) "" else lineSeparator)
      }).use(Task(_))
        .runSyncUnsafe()
    }
  }

  it should "write in async mode" in {
    forAll { (loggerMessage: List[LoggerMessage], formatter: Formatter) =>
      (for {
        path <- fileResource
        fileName = path.toString
        logger <- asyncFileLogger[Task](fileName, formatter)
        _ <- Resource.liftF(logger.withMinimalLevel(Level.Trace).log(loggerMessage))
        _ <- Resource.liftF(Task(scheduler.tick(2.seconds)))
      } yield {
        new String(Files.readAllBytes(Paths.get(fileName))) shouldBe loggerMessage
          .map(formatter.format)
          .mkString(lineSeparator) + (if (loggerMessage.isEmpty) "" else lineSeparator)
      }).use(Task(_))
        .runSyncUnsafe()
    }
  }
} 
Example 102
Source File: S3MonixClientSupport.scala    From reactive-aws-clients   with MIT License 5 votes vote down vote up
package com.github.j5ik2o.reactive.aws.s3.monix

import java.io.File
import java.nio.file.Path

import monix.eval.Task
import software.amazon.awssdk.core.ResponseBytes
import software.amazon.awssdk.core.async.{ AsyncRequestBody, AsyncResponseTransformer }
import software.amazon.awssdk.services.s3.model._

trait S3MonixClientSupport { this: S3MonixClient =>
  override type RT[A, B] = AsyncResponseTransformer[A, B]
  override type RB       = AsyncRequestBody

  override def listBuckets(): Task[ListBucketsResponse] = Task.deferFuture {
    underlying.listBuckets()
  }

  override def getObjectAsBytes(getObjectRequest: GetObjectRequest): Task[ResponseBytes[GetObjectResponse]] =
    Task.deferFuture {
      underlying.getObjectAsBytes(getObjectRequest)
    }

  override def getObjectToFile(getObjectRequest: GetObjectRequest, file: File): Task[GetObjectResponse] =
    Task.deferFuture {
      underlying.getObjectToFile(getObjectRequest, file)
    }

  override def getObjectToPath(getObjectRequest: GetObjectRequest, destinationPath: Path): Task[GetObjectResponse] =
    Task.deferFuture {
      underlying.getObjectToPath(getObjectRequest, destinationPath)
    }

  override def getObject[A](
      getObjectRequest: GetObjectRequest,
      responseTransformer: AsyncResponseTransformer[GetObjectResponse, A]
  ): Task[A] =
    Task.deferFuture {
      underlying.getObject(getObjectRequest, responseTransformer)
    }

  override def getObjectTorrentAsBytes(
      getObjectRequest: GetObjectTorrentRequest
  ): Task[ResponseBytes[GetObjectTorrentResponse]] = Task.deferFuture {
    underlying.getObjectTorrentAsBytes(getObjectRequest)
  }

  override def getObjectTorrentToFile(
      getObjectRequest: GetObjectTorrentRequest,
      file: File
  ): Task[GetObjectTorrentResponse] = Task.deferFuture {
    underlying.getObjectTorrentToFile(getObjectRequest, file)
  }

  override def getObjectTorrentToPath(
      getObjectTorrentRequest: GetObjectTorrentRequest,
      destinationPath: Path
  ): Task[GetObjectTorrentResponse] = Task.deferFuture {
    underlying.getObjectTorrentToPath(getObjectTorrentRequest, destinationPath)
  }

  override def getObjectTorrent[A](
      getObjectTorrentRequest: GetObjectTorrentRequest,
      responseTransformer: AsyncResponseTransformer[GetObjectTorrentResponse, A]
  ): Task[A] = Task.deferFuture {
    underlying.getObjectTorrent(getObjectTorrentRequest, responseTransformer)
  }

  override def putObject(putObjectRequest: PutObjectRequest, requestBody: AsyncRequestBody): Task[PutObjectResponse] =
    Task.deferFuture {
      underlying.putObject(putObjectRequest, requestBody)
    }

  override def putObjectFromPath(putObjectRequest: PutObjectRequest, sourcePath: Path): Task[PutObjectResponse] =
    Task.deferFuture {
      underlying.putObjectFromPath(putObjectRequest, sourcePath)
    }

  override def putObjectFromFile(putObjectRequest: PutObjectRequest, sourceFile: File): Task[PutObjectResponse] =
    Task.deferFuture {
      underlying.putObjectFromFile(putObjectRequest, sourceFile)
    }

  override def uploadPart(
      uploadPartRequest: UploadPartRequest,
      requestBody: AsyncRequestBody
  ): Task[UploadPartResponse] = Task.deferFuture {
    underlying.uploadPart(uploadPartRequest, requestBody)
  }

  override def uploadPartFromPath(uploadPartRequest: UploadPartRequest, sourcePath: Path): Task[UploadPartResponse] =
    Task.deferFuture {
      underlying.uploadPartFromPath(uploadPartRequest, sourcePath)
    }

  override def uploadPartFromFile(uploadPartRequest: UploadPartRequest, sourceFile: File): Task[UploadPartResponse] =
    Task.deferFuture {
      underlying.uploadPartFromFile(uploadPartRequest, sourceFile)
    }
} 
Example 103
Source File: DynamoDbStreamsMonixClient.scala    From reactive-aws-clients   with MIT License 5 votes vote down vote up
// Auto-Generated
package com.github.j5ik2o.reactive.aws.dynamodb.streams.monix

import com.github.j5ik2o.reactive.aws.dynamodb.streams.{ DynamoDbStreamsAsyncClient, DynamoDbStreamsClient }
import monix.eval.Task
import monix.reactive.Observable
import software.amazon.awssdk.services.dynamodb.model._

object DynamoDbStreamsMonixClient {

  def apply(underlying: DynamoDbStreamsAsyncClient): DynamoDbStreamsMonixClient =
    new DynamoDbStreamsMonixClientImpl(underlying)

}

trait DynamoDbStreamsMonixClient extends DynamoDbStreamsClient[Task] {

  val underlying: DynamoDbStreamsAsyncClient

  override def describeStream(describeStreamRequest: DescribeStreamRequest): Task[DescribeStreamResponse] =
    Task.deferFuture {
      underlying.describeStream(describeStreamRequest)
    }

  def describeStreamPaginator(describeStreamRequest: DescribeStreamRequest): Observable[DescribeStreamResponse] =
    Observable.fromReactivePublisher(underlying.describeStreamPaginator(describeStreamRequest))

  override def getRecords(getRecordsRequest: GetRecordsRequest): Task[GetRecordsResponse] =
    Task.deferFuture {
      underlying.getRecords(getRecordsRequest)
    }

  override def getShardIterator(getShardIteratorRequest: GetShardIteratorRequest): Task[GetShardIteratorResponse] =
    Task.deferFuture {
      underlying.getShardIterator(getShardIteratorRequest)
    }

  override def listStreams(listStreamsRequest: ListStreamsRequest): Task[ListStreamsResponse] =
    Task.deferFuture {
      underlying.listStreams(listStreamsRequest)
    }

  override def listStreams(): Task[ListStreamsResponse] =
    Task.deferFuture {
      underlying.listStreams()
    }

  def listStreamsPaginator(): Observable[ListStreamsResponse] =
    Observable.fromReactivePublisher(underlying.listStreamsPaginator())

  def listStreamsPaginator(listStreamsRequest: ListStreamsRequest): Observable[ListStreamsResponse] =
    Observable.fromReactivePublisher(underlying.listStreamsPaginator(listStreamsRequest))

} 
Example 104
Source File: Runner.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.monix
import io.getquill.context.ContextEffect
import monix.eval.Task
import monix.execution.Scheduler
import monix.reactive.Observable

object Runner {
  def default = new Runner {}
  def using(scheduler: Scheduler) = new Runner {
    override def schedule[T](t: Task[T]): Task[T] = t.executeOn(scheduler, true)
    override def boundary[T](t: Task[T]): Task[T] = t.executeOn(scheduler, true)
    override def scheduleObservable[T](o: Observable[T]): Observable[T] = o.executeOn(scheduler, true)
  }
}

trait Runner extends ContextEffect[Task] {
  override def wrap[T](t: => T): Task[T] = Task(t)
  override def push[A, B](result: Task[A])(f: A => B): Task[B] = result.map(f)
  override def seq[A, B](list: List[Task[A]]): Task[List[A]] = Task.sequence(list)
  def schedule[T](t: Task[T]): Task[T] = t
  def scheduleObservable[T](o: Observable[T]): Observable[T] = o
  def boundary[T](t: Task[T]): Task[T] = t.asyncBoundary

  
  def wrapClose(t: => Unit): Task[Unit] = Task(t)
} 
Example 105
Source File: MonixContext.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.monix

import io.getquill.NamingStrategy
import io.getquill.context.{ Context, StreamingContext }
import monix.eval.Task
import monix.reactive.Observable

trait MonixContext[Idiom <: io.getquill.idiom.Idiom, Naming <: NamingStrategy] extends Context[Idiom, Naming]
  with StreamingContext[Idiom, Naming] {

  override type StreamResult[T] = Observable[T]
  override type Result[T] = Task[T]
  override type RunQueryResult[T] = List[T]
  override type RunQuerySingleResult[T] = T

  // Need explicit return-type annotations due to scala/bug#8356. Otherwise macro system will not understand Result[Long]=Task[Long] etc...
  def executeQuery[T](sql: String, prepare: Prepare = identityPrepare, extractor: Extractor[T] = identityExtractor): Task[List[T]]
  def executeQuerySingle[T](sql: String, prepare: Prepare = identityPrepare, extractor: Extractor[T] = identityExtractor): Task[T]

  protected val effect: Runner
} 
Example 106
Source File: RunnerSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.monix

import io.getquill.Spec
import monix.eval.Task
import monix.execution.Scheduler
import monix.execution.schedulers.CanBlock
import org.scalatest.matchers.should.Matchers._

import scala.util.Failure

class RunnerSpec extends Spec {

  class SideEffect {
    private var state = 0
    def apply() = state = 1
    def applied = state == 1
  }

  implicit val scheduler = Scheduler.global

  "plain runner" - {
    val runner = Runner.default
    import runner._

    "should lazily evaluate" in {
      val sideEffect = new SideEffect
      val task = wrap(sideEffect())
      sideEffect.applied should equal(false)
      task.runSyncUnsafe()
      sideEffect.applied should equal(true)
    }

    "should encapsulate exception throw" in {
      wrap(throw new RuntimeException("Surprise!")).materialize.runSyncUnsafe() should matchPattern {
        case Failure(e) if (e.getMessage == "Surprise!") =>
      }
    }

    "should push an effect correctly" in {
      push(Task(1))(_ + 1).runSyncUnsafe() should equal(2)
    }

    "should convert a sequence correctly" in {
      seq(List(Task(1), Task(2), Task(3))).runSyncUnsafe() should equal(List(1, 2, 3))
    }

    "plain schedule should be a no-op" in {
      val t = Task(1)
      (schedule(t) eq (t)) must equal(true)
    }

    "boundary operator must force async boundary" in {
      val (first, second) =
        boundary(Task(Thread.currentThread().getName))
          .flatMap(prevName => Task((prevName, Thread.currentThread().getName)))
          .runSyncUnsafe()
      first must not equal (second)
    }
  }

  "using scheduler runner" - {
    val prefix = "quill-test-pool"
    val customScheduler = Scheduler.io(prefix)
    val runner = Runner.using(customScheduler)
    import runner._

    "should run in specified scheduler" in {
      // the global scheduler is imported but want to explicitly tell this to run on it, just for clarity
      val threadName =
        schedule(Task(Thread.currentThread().getName)).runSyncUnsafe()(Scheduler.global, CanBlock.permit)

      threadName.startsWith(prefix) must equal(true)
    }

    "should async-boundary in specified scheduler" in {
      // the global scheduler is imported but want to explicitly tell this to run on it, just for clarity
      val threadName =
        boundary(Task(Thread.currentThread().getName)).runSyncUnsafe()(Scheduler.global, CanBlock.permit)

      threadName.startsWith(prefix) must equal(true)
    }

    "should async-boundary correctly" in {
      val prefix2 = "quill-test-pool2"
      // the global scheduler is imported but want to explicitly tell this to run on it, just for clarity
      val (first, second) =
        Task(Thread.currentThread().getName)
          .executeOn(Scheduler.io(prefix2))
          .flatMap(prevName => boundary(Task((prevName, Thread.currentThread().getName))))
          .runSyncUnsafe()(Scheduler.global, CanBlock.permit)

      first.startsWith(prefix2) must equal(true)
      second.startsWith(prefix) must equal(true)
      first must not equal second
    }
  }
} 
Example 107
Source File: CassandraStreamContext.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill

import com.datastax.driver.core.{ Cluster, ResultSet, Row }
import com.typesafe.config.Config
import io.getquill.context.cassandra.util.FutureConversions._
import io.getquill.util.{ ContextLogger, LoadConfig }
import monix.eval.Task
import monix.execution.Scheduler
import monix.execution.Scheduler.Implicits
import monix.reactive.Observable

import scala.jdk.CollectionConverters._
import scala.util.{ Failure, Success }

class CassandraStreamContext[N <: NamingStrategy](
  naming:                     N,
  cluster:                    Cluster,
  keyspace:                   String,
  preparedStatementCacheSize: Long
)
  extends CassandraClusterSessionContext[N](naming, cluster, keyspace, preparedStatementCacheSize) {

  def this(naming: N, config: CassandraContextConfig) = this(naming, config.cluster, config.keyspace, config.preparedStatementCacheSize)
  def this(naming: N, config: Config) = this(naming, CassandraContextConfig(config))
  def this(naming: N, configPrefix: String) = this(naming, LoadConfig(configPrefix))

  private val logger = ContextLogger(classOf[CassandraStreamContext[_]])

  override type Result[T] = Observable[T]
  override type RunQueryResult[T] = T
  override type RunQuerySingleResult[T] = T
  override type RunActionResult = Unit
  override type RunBatchActionResult = Unit

  protected def page(rs: ResultSet): Task[Iterable[Row]] = Task.defer {
    val available = rs.getAvailableWithoutFetching
    val page = rs.asScala.take(available)

    if (rs.isFullyFetched)
      Task.now(page)
    else
      Task.fromFuture(rs.fetchMoreResults().asScala(Implicits.global)).map(_ => page)
  }

  def executeQuery[T](cql: String, prepare: Prepare = identityPrepare, extractor: Extractor[T] = identityExtractor): Observable[T] = {

    Observable
      .fromTask(prepareRowAndLog(cql, prepare))
      .mapEvalF(p => session.executeAsync(p).asScala(Implicits.global))
      .flatMap(Observable.fromAsyncStateAction((rs: ResultSet) => page(rs).map((_, rs)))(_))
      .takeWhile(_.nonEmpty)
      .flatMap(Observable.fromIterable)
      .map(extractor)
  }

  def executeQuerySingle[T](cql: String, prepare: Prepare = identityPrepare, extractor: Extractor[T] = identityExtractor): Observable[T] =
    executeQuery(cql, prepare, extractor)

  def executeAction[T](cql: String, prepare: Prepare = identityPrepare): Observable[Unit] = {
    Observable
      .fromTask(prepareRowAndLog(cql, prepare))
      .mapEvalF(p => session.executeAsync(p).asScala(Implicits.global))
      .map(_ => ())
  }

  def executeBatchAction(groups: List[BatchGroup]): Observable[Unit] =
    Observable.fromIterable(groups).flatMap {
      case BatchGroup(cql, prepare) =>
        Observable.fromIterable(prepare)
          .flatMap(executeAction(cql, _))
          .map(_ => ())
    }

  private def prepareRowAndLog(cql: String, prepare: Prepare = identityPrepare): Task[PrepareRow] = {
    Task.async0[PrepareRow] { (scheduler, callback) =>
      implicit val executor: Scheduler = scheduler

      super.prepareAsync(cql)
        .map(prepare)
        .onComplete {
          case Success((params, bs)) =>
            logger.logQuery(cql, params)
            callback.onSuccess(bs)
          case Failure(ex) =>
            callback.onError(ex)
        }
    }
  }
} 
Example 108
Source File: CassandraMonixContext.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill

import com.datastax.driver.core.{ Cluster, ResultSet, Row }
import com.typesafe.config.Config
import io.getquill.context.cassandra.CqlIdiom
import io.getquill.context.monix.{ MonixContext, Runner }
import io.getquill.util.{ ContextLogger, LoadConfig }
import io.getquill.context.cassandra.util.FutureConversions._
import monix.eval.Task
import monix.execution.Scheduler
import monix.reactive.Observable

import scala.jdk.CollectionConverters._
import scala.util.{ Failure, Success }

class CassandraMonixContext[N <: NamingStrategy](
  naming:                     N,
  cluster:                    Cluster,
  keyspace:                   String,
  preparedStatementCacheSize: Long
)
  extends CassandraClusterSessionContext[N](naming, cluster, keyspace, preparedStatementCacheSize)
  with MonixContext[CqlIdiom, N] {

  // not using this here
  override val effect = Runner.default

  def this(naming: N, config: CassandraContextConfig) = this(naming, config.cluster, config.keyspace, config.preparedStatementCacheSize)
  def this(naming: N, config: Config) = this(naming, CassandraContextConfig(config))
  def this(naming: N, configPrefix: String) = this(naming, LoadConfig(configPrefix))

  private val logger = ContextLogger(classOf[CassandraMonixContext[_]])

  override type StreamResult[T] = Observable[T]
  override type RunActionResult = Unit
  override type Result[T] = Task[T]

  override type RunQueryResult[T] = List[T]
  override type RunQuerySingleResult[T] = T
  override type RunBatchActionResult = Unit

  protected def page(rs: ResultSet): Task[Iterable[Row]] = Task.defer {
    val available = rs.getAvailableWithoutFetching
    val page = rs.asScala.take(available)

    if (rs.isFullyFetched)
      Task.now(page)
    else
      Task.fromFuture(rs.fetchMoreResults().asScalaWithDefaultGlobal).map(_ => page)
  }

  def streamQuery[T](fetchSize: Option[Int], cql: String, prepare: Prepare = identityPrepare, extractor: Extractor[T] = identityExtractor): Observable[T] = {

    Observable
      .fromTask(prepareRowAndLog(cql, prepare))
      .mapEvalF(p => session.executeAsync(p).asScalaWithDefaultGlobal)
      .flatMap(Observable.fromAsyncStateAction((rs: ResultSet) => page(rs).map((_, rs)))(_))
      .takeWhile(_.nonEmpty)
      .flatMap(Observable.fromIterable)
      .map(extractor)
  }

  def executeQuery[T](cql: String, prepare: Prepare = identityPrepare, extractor: Extractor[T] = identityExtractor): Task[List[T]] = {
    streamQuery[T](None, cql, prepare, extractor)
      .foldLeftL(List[T]())({ case (l, r) => r +: l }).map(_.reverse)
  }

  def executeQuerySingle[T](cql: String, prepare: Prepare = identityPrepare, extractor: Extractor[T] = identityExtractor): Task[T] =
    executeQuery(cql, prepare, extractor).map(handleSingleResult(_))

  def executeAction[T](cql: String, prepare: Prepare = identityPrepare): Task[Unit] = {
    prepareRowAndLog(cql, prepare)
      .flatMap(r => Task.fromFuture(session.executeAsync(r).asScalaWithDefaultGlobal))
      .map(_ => ())
  }

  def executeBatchAction(groups: List[BatchGroup]): Task[Unit] =
    Observable.fromIterable(groups).flatMap {
      case BatchGroup(cql, prepare) =>
        Observable.fromIterable(prepare)
          .flatMap(prep => Observable.fromTask(executeAction(cql, prep)))
          .map(_ => ())
    }.completedL

  private def prepareRowAndLog(cql: String, prepare: Prepare = identityPrepare): Task[PrepareRow] = {
    Task.async0[PrepareRow] { (scheduler, callback) =>
      implicit val executor: Scheduler = scheduler

      super.prepareAsync(cql)
        .map(prepare)
        .onComplete {
          case Success((params, bs)) =>
            logger.logQuery(cql, params)
            callback.onSuccess(bs)
          case Failure(ex) =>
            callback.onError(ex)
        }
    }
  }
} 
Example 109
Source File: QueryResultTypeCassandraMonixSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.cassandra.monix

import io.getquill.context.cassandra.QueryResultTypeCassandraSpec
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import monix.reactive.Observable

class QueryResultTypeCassandraMonixSpec extends QueryResultTypeCassandraSpec {

  val context = testMonixDB

  import context._

  def result[T](t: Task[T]) =
    await(t.runToFuture(global))

  def result[T](t: Observable[T]) =
    await(t.foldLeftL(List.empty[T])(_ :+ _).runToFuture)

  override def beforeAll = {
    result(context.run(deleteAll))
    result(context.run(liftQuery(entries).foreach(e => insert(e))))
    ()
  }

  "query" in {
    result(context.run(selectAll)) mustEqual entries
  }

  "stream" in {
    result(context.stream(selectAll)) mustEqual entries
  }

  "querySingle" - {
    "size" in {
      result(context.run(entitySize)) mustEqual 3
    }
    "parametrized size" in {
      result(context.run(parametrizedSize(lift(10000)))) mustEqual 0
    }
  }
} 
Example 110
Source File: PrepareMonixJdbcSpecBase.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill

import java.sql.{ Connection, PreparedStatement, ResultSet }

import io.getquill.context.jdbc.ResultSetExtractor
import io.getquill.context.sql.ProductSpec
import monix.eval.Task
import org.scalactic.Equality

trait PrepareMonixJdbcSpecBase extends ProductSpec {

  implicit val productEq = new Equality[Product] {
    override def areEqual(a: Product, b: Any): Boolean = b match {
      case Product(_, desc, sku) => desc == a.description && sku == a.sku
      case _                     => false
    }
  }

  def productExtractor: ResultSet => Product

  def withOrderedIds(products: List[Product]) =
    products.zipWithIndex.map { case (product, id) => product.copy(id = id.toLong + 1) }

  def singleInsert(conn: => Connection)(prep: Connection => Task[PreparedStatement]) = {
    Task(conn).bracket { conn =>
      prep(conn).bracket { stmt =>
        Task(stmt.execute())
      }(stmt => Task(stmt.close()))
    }(conn => Task(conn.close()))
  }

  def batchInsert(conn: => Connection)(prep: Connection => Task[List[PreparedStatement]]) = {
    Task(conn).bracket { conn =>
      prep(conn).flatMap(stmts =>
        Task.sequence(
          stmts.map(stmt =>
            Task(stmt).bracket { stmt =>
              Task(stmt.execute())
            }(stmt => Task(stmt.close())))
        ))
    }(conn => Task(conn.close()))
  }

  def extractResults[T](conn: => Connection)(prep: Connection => Task[PreparedStatement])(extractor: ResultSet => T) = {
    Task(conn).bracket { conn =>
      prep(conn).bracket { stmt =>
        Task(stmt.executeQuery()).bracket { rs =>
          Task(ResultSetExtractor(rs, extractor))
        }(rs => Task(rs.close()))
      }(stmt => Task(stmt.close()))
    }(conn => Task(conn.close()))
  }

  def extractProducts(conn: => Connection)(prep: Connection => Task[PreparedStatement]) =
    extractResults(conn)(prep)(productExtractor)
} 
Example 111
Source File: MonixJdbcContextSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.oracle

import io.getquill.MonixSpec
import monix.eval.Task

class MonixJdbcContextSpec extends MonixSpec {

  val context = testContext
  import testContext._

  "provides transaction support" - {
    "success" in {
      (for {
        _ <- testContext.run(qr1.delete)
        _ <- testContext.transaction {
          testContext.run(qr1.insert(_.i -> 33))
        }
        r <- testContext.run(qr1)
      } yield r).runSyncUnsafe().map(_.i) mustEqual List(33)
    }
    "success - stream" in {
      (for {
        _ <- testContext.run(qr1.delete)
        seq <- testContext.transaction {
          for {
            _ <- testContext.run(qr1.insert(_.i -> 33))
            s <- accumulate(testContext.stream(qr1))
          } yield s
        }
        r <- testContext.run(qr1)
      } yield (seq.map(_.i), r.map(_.i))).runSyncUnsafe() mustEqual ((List(33), List(33)))
    }
    "failure" in {
      (for {
        _ <- testContext.run(qr1.delete)
        e <- testContext.transaction {
          Task.sequence(Seq(
            testContext.run(qr1.insert(_.i -> 18)),
            Task.eval {
              throw new IllegalStateException
            }
          ))
        }.onErrorHandleWith {
          case e: Exception => Task(e.getClass.getSimpleName)
        }
        r <- testContext.run(qr1)
      } yield (e, r.isEmpty)).runSyncUnsafe() mustEqual (("IllegalStateException", true))
    }
    "nested" in {
      (for {
        _ <- testContext.run(qr1.delete)
        _ <- testContext.transaction {
          testContext.transaction {
            testContext.run(qr1.insert(_.i -> 33))
          }
        }
        r <- testContext.run(qr1)
      } yield r).runSyncUnsafe().map(_.i) mustEqual List(33)
    }
    "prepare" in {
      testContext.prepareParams(
        "select * from Person where name=? and age > ?", ps => (List("Sarah", 127), ps)
      ).runSyncUnsafe() mustEqual List("127", "'Sarah'")
    }
  }
} 
Example 112
Source File: ProductJdbcSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.oracle

import io.getquill.context.sql.ProductSpec
import monix.eval.Task
import monix.execution.Scheduler

class ProductJdbcSpec extends ProductSpec {

  val context = testContext
  import testContext._

  implicit val scheduler = Scheduler.global

  override def beforeAll = {
    testContext.run(quote(query[Product].delete)).runSyncUnsafe()
    ()
  }

  "Product" - {
    "Insert multiple products" in {
      val (inserted, product) =
        (for {
          i <- Task.sequence(productEntries.map(product => testContext.run(productInsert(lift(product)))))
          ps <- testContext.run(productById(lift(i(2))))
        } yield (i, ps.head)).runSyncUnsafe()

      product.description mustEqual productEntries(2).description
      product.id mustEqual inserted(2)
    }

    "Single insert product" in {
      val (inserted, product) =
        (for {
          i <- testContext.run(productSingleInsert)
          ps <- testContext.run(productById(lift(i)))
        } yield (i, ps.head)).runSyncUnsafe()
      product.description mustEqual "Window"
      product.id mustEqual inserted
    }

    "Single insert with inlined free variable" in {
      val prd = Product(0L, "test1", 1L)
      val (inserted, returnedProduct) =
        (for {
          i <- testContext.run {
            product.insert(_.sku -> lift(prd.sku), _.description -> lift(prd.description)).returning(_.id)
          }
          rps <- testContext.run(productById(lift(i)))
        } yield (i, rps.head)).runSyncUnsafe()

      returnedProduct.description mustEqual "test1"
      returnedProduct.sku mustEqual 1L
      returnedProduct.id mustEqual inserted
    }

    "Single insert with free variable and explicit quotation" in {
      val prd = Product(0L, "test2", 2L)
      val q1 = quote {
        product.insert(_.sku -> lift(prd.sku), _.description -> lift(prd.description)).returning(_.id)
      }
      val (inserted, returnedProduct) =
        (for {
          i <- testContext.run(q1)
          rps <- testContext.run(productById(lift(i)))
        } yield (i, rps.head)).runSyncUnsafe()

      returnedProduct.description mustEqual "test2"
      returnedProduct.sku mustEqual 2L
      returnedProduct.id mustEqual inserted
    }

    "Single product insert with a method quotation" in {
      val prd = Product(0L, "test3", 3L)
      val (inserted, returnedProduct) =
        (for {
          i <- testContext.run(productInsert(lift(prd)))
          rps <- testContext.run(productById(lift(i)))
        } yield (i, rps.head)).runSyncUnsafe()

      returnedProduct.description mustEqual "test3"
      returnedProduct.sku mustEqual 3L
      returnedProduct.id mustEqual inserted
    }
  }
} 
Example 113
Source File: MonixJdbcContextSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.postgres

import io.getquill.MonixSpec
import monix.eval.Task

class MonixJdbcContextSpec extends MonixSpec {

  val context = testContext
  import testContext._

  "provides transaction support" - {
    "success" in {
      (for {
        _ <- testContext.run(qr1.delete)
        _ <- testContext.transaction {
          testContext.run(qr1.insert(_.i -> 33))
        }
        r <- testContext.run(qr1)
      } yield r).runSyncUnsafe().map(_.i) mustEqual List(33)
    }
    "success - stream" in {
      (for {
        _ <- testContext.run(qr1.delete)
        seq <- testContext.transaction {
          for {
            _ <- testContext.run(qr1.insert(_.i -> 33))
            s <- accumulate(testContext.stream(qr1))
          } yield s
        }
        r <- testContext.run(qr1)
      } yield (seq.map(_.i), r.map(_.i))).runSyncUnsafe() mustEqual ((List(33), List(33)))
    }
    "failure" in {
      (for {
        _ <- testContext.run(qr1.delete)
        e <- testContext.transaction {
          Task.sequence(Seq(
            testContext.run(qr1.insert(_.i -> 18)),
            Task.eval {
              throw new IllegalStateException
            }
          ))
        }.onErrorHandleWith {
          case e: Exception => Task(e.getClass.getSimpleName)
        }
        r <- testContext.run(qr1)
      } yield (e, r.isEmpty)).runSyncUnsafe() mustEqual (("IllegalStateException", true))
    }
    "nested" in {
      (for {
        _ <- testContext.run(qr1.delete)
        _ <- testContext.transaction {
          testContext.transaction {
            testContext.run(qr1.insert(_.i -> 33))
          }
        }
        r <- testContext.run(qr1)
      } yield r).runSyncUnsafe().map(_.i) mustEqual List(33)
    }
    "prepare" in {
      testContext.prepareParams(
        "select * from Person where name=? and age > ?", ps => (List("Sarah", 127), ps)
      ).runSyncUnsafe() mustEqual List("127", "'Sarah'")
    }
  }
} 
Example 114
Source File: MonixJdbcContextSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.sqlserver

import io.getquill.MonixSpec
import monix.eval.Task

class MonixJdbcContextSpec extends MonixSpec {

  val context = testContext
  import testContext._

  "provides transaction support" - {
    "success" in {
      (for {
        _ <- testContext.run(qr1.delete)
        _ <- testContext.transaction {
          testContext.run(qr1.insert(_.i -> 33))
        }
        r <- testContext.run(qr1)
      } yield r).runSyncUnsafe().map(_.i) mustEqual List(33)
    }
    "success - stream" in {
      (for {
        _ <- testContext.run(qr1.delete)
        seq <- testContext.transaction {
          for {
            _ <- testContext.run(qr1.insert(_.i -> 33))
            s <- accumulate(testContext.stream(qr1))
          } yield s
        }
        r <- testContext.run(qr1)
      } yield (seq.map(_.i), r.map(_.i))).runSyncUnsafe() mustEqual ((List(33), List(33)))
    }
    "failure" in {
      (for {
        _ <- testContext.run(qr1.delete)
        e <- testContext.transaction {
          Task.sequence(Seq(
            testContext.run(qr1.insert(_.i -> 18)),
            Task.eval {
              throw new IllegalStateException
            }
          ))
        }.onErrorHandleWith {
          case e: Exception => Task(e.getClass.getSimpleName)
        }
        r <- testContext.run(qr1)
      } yield (e, r.isEmpty)).runSyncUnsafe() mustEqual (("IllegalStateException", true))
    }
    "nested" in {
      (for {
        _ <- testContext.run(qr1.delete)
        _ <- testContext.transaction {
          testContext.transaction {
            testContext.run(qr1.insert(_.i -> 33))
          }
        }
        r <- testContext.run(qr1)
      } yield r).runSyncUnsafe().map(_.i) mustEqual List(33)
    }
    "prepare" in {
      testContext.prepareParams(
        "select * from Person where name=? and age > ?", ps => (List("Sarah", 127), ps)
      ).runSyncUnsafe() mustEqual List("127", "'Sarah'")
    }
  }
} 
Example 115
Source File: ProductJdbcSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.sqlserver

import io.getquill.context.sql.ProductSpec
import monix.eval.Task
import monix.execution.Scheduler

class ProductJdbcSpec extends ProductSpec {

  val context = testContext
  import testContext._

  implicit val scheduler = Scheduler.global

  override def beforeAll = {
    testContext.run(quote(query[Product].delete)).runSyncUnsafe()
    ()
  }

  "Product" - {
    "Insert multiple products" in {
      val (inserted, product) =
        (for {
          i <- Task.sequence(productEntries.map(product => testContext.run(productInsert(lift(product)))))
          ps <- testContext.run(productById(lift(i(2))))
        } yield (i, ps.head)).runSyncUnsafe()

      product.description mustEqual productEntries(2).description
      product.id mustEqual inserted(2)
    }

    "Single insert product" in {
      val (inserted, product) =
        (for {
          i <- testContext.run(productSingleInsert)
          ps <- testContext.run(productById(lift(i)))
        } yield (i, ps.head)).runSyncUnsafe()
      product.description mustEqual "Window"
      product.id mustEqual inserted
    }

    "Single insert with inlined free variable" in {
      val prd = Product(0L, "test1", 1L)
      val (inserted, returnedProduct) =
        (for {
          i <- testContext.run {
            product.insert(_.sku -> lift(prd.sku), _.description -> lift(prd.description)).returningGenerated(_.id)
          }
          rps <- testContext.run(productById(lift(i)))
        } yield (i, rps.head)).runSyncUnsafe()

      returnedProduct.description mustEqual "test1"
      returnedProduct.sku mustEqual 1L
      returnedProduct.id mustEqual inserted
    }

    "Single insert with free variable and explicit quotation" in {
      val prd = Product(0L, "test2", 2L)
      val q1 = quote {
        product.insert(_.sku -> lift(prd.sku), _.description -> lift(prd.description)).returningGenerated(_.id)
      }
      val (inserted, returnedProduct) =
        (for {
          i <- testContext.run(q1)
          rps <- testContext.run(productById(lift(i)))
        } yield (i, rps.head)).runSyncUnsafe()

      returnedProduct.description mustEqual "test2"
      returnedProduct.sku mustEqual 2L
      returnedProduct.id mustEqual inserted
    }

    "Single product insert with a method quotation" in {
      val prd = Product(0L, "test3", 3L)
      val (inserted, returnedProduct) =
        (for {
          i <- testContext.run(productInsert(lift(prd)))
          rps <- testContext.run(productById(lift(i)))
        } yield (i, rps.head)).runSyncUnsafe()

      returnedProduct.description mustEqual "test3"
      returnedProduct.sku mustEqual 3L
      returnedProduct.id mustEqual inserted
    }
  }
} 
Example 116
Source File: MonixJdbcContextSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.mysql

import io.getquill.MonixSpec
import monix.eval.Task

class MonixJdbcContextSpec extends MonixSpec {

  val context = testContext
  import testContext._

  "provides transaction support" - {
    "success" in {
      (for {
        _ <- testContext.run(qr1.delete)
        _ <- testContext.transaction {
          testContext.run(qr1.insert(_.i -> 33))
        }
        r <- testContext.run(qr1)
      } yield r).runSyncUnsafe().map(_.i) mustEqual List(33)
    }
    "success - stream" in {
      (for {
        _ <- testContext.run(qr1.delete)
        seq <- testContext.transaction {
          for {
            _ <- testContext.run(qr1.insert(_.i -> 33))
            s <- accumulate(testContext.stream(qr1))
          } yield s
        }
        r <- testContext.run(qr1)
      } yield (seq.map(_.i), r.map(_.i))).runSyncUnsafe() mustEqual ((List(33), List(33)))
    }
    "failure" in {
      (for {
        _ <- testContext.run(qr1.delete)
        e <- testContext.transaction {
          Task.sequence(Seq(
            testContext.run(qr1.insert(_.i -> 18)),
            Task.eval {
              throw new IllegalStateException
            }
          ))
        }.onErrorHandleWith {
          case e: Exception => Task(e.getClass.getSimpleName)
        }
        r <- testContext.run(qr1)
      } yield (e, r.isEmpty)).runSyncUnsafe() mustEqual (("IllegalStateException", true))
    }
    "nested" in {
      (for {
        _ <- testContext.run(qr1.delete)
        _ <- testContext.transaction {
          testContext.transaction {
            testContext.run(qr1.insert(_.i -> 33))
          }
        }
        r <- testContext.run(qr1)
      } yield r).runSyncUnsafe().map(_.i) mustEqual List(33)
    }
    "prepare" in {
      testContext.prepareParams(
        "select * from Person where name=? and age > ?", ps => (List("Sarah", 127), ps)
      ).runSyncUnsafe() mustEqual List("127", "'Sarah'")
    }
  }
} 
Example 117
Source File: MonixJdbcContextSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.sqlite

import io.getquill.MonixSpec
import monix.eval.Task

class MonixJdbcContextSpec extends MonixSpec {

  val context = testContext
  import testContext._

  "provides transaction support" - {
    "success" in {
      (for {
        _ <- testContext.run(qr1.delete)
        _ <- testContext.transaction {
          testContext.run(qr1.insert(_.i -> 33))
        }
        r <- testContext.run(qr1)
      } yield r).runSyncUnsafe().map(_.i) mustEqual List(33)
    }
    "success - stream" in {
      (for {
        _ <- testContext.run(qr1.delete)
        seq <- testContext.transaction {
          for {
            _ <- testContext.run(qr1.insert(_.i -> 33))
            s <- accumulate(testContext.stream(qr1))
          } yield s
        }
        r <- testContext.run(qr1)
      } yield (seq.map(_.i), r.map(_.i))).runSyncUnsafe() mustEqual ((List(33), List(33)))
    }
    "failure" in {
      (for {
        _ <- testContext.run(qr1.delete)
        e <- testContext.transaction {
          Task.sequence(Seq(
            testContext.run(qr1.insert(_.i -> 18)),
            Task.eval {
              throw new IllegalStateException
            }
          ))
        }.onErrorHandleWith {
          case e: Exception => Task(e.getClass.getSimpleName)
        }
        r <- testContext.run(qr1)
      } yield (e, r.isEmpty)).runSyncUnsafe() mustEqual (("IllegalStateException", true))
    }
    "nested" in {
      (for {
        _ <- testContext.run(qr1.delete)
        _ <- testContext.transaction {
          testContext.transaction {
            testContext.run(qr1.insert(_.i -> 33))
          }
        }
        r <- testContext.run(qr1)
      } yield r).runSyncUnsafe().map(_.i) mustEqual List(33)
    }
    "prepare" in {
      testContext.prepareParams(
        "select * from Person where name=? and age > ?", ps => (List("Sarah", 127), ps)
      ).runSyncUnsafe() mustEqual List("127", "'Sarah'")
    }
  }
} 
Example 118
Source File: ProductJdbcSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.sqlite

import io.getquill.context.sql.ProductSpec
import monix.eval.Task
import monix.execution.Scheduler

class ProductJdbcSpec extends ProductSpec {

  val context = testContext
  import testContext._

  implicit val scheduler = Scheduler.global

  override def beforeAll = {
    testContext.run(quote(query[Product].delete)).runSyncUnsafe()
    ()
  }

  "Product" - {
    "Insert multiple products" in {
      val (inserted, product) =
        (for {
          i <- Task.sequence(productEntries.map(product => testContext.run(productInsert(lift(product)))))
          ps <- testContext.run(productById(lift(i(2))))
        } yield (i, ps.head)).runSyncUnsafe()

      product.description mustEqual productEntries(2).description
      product.id mustEqual inserted(2)
    }

    "Single insert product" in {
      val (inserted, product) =
        (for {
          i <- testContext.run(productSingleInsert)
          ps <- testContext.run(productById(lift(i)))
        } yield (i, ps.head)).runSyncUnsafe()
      product.description mustEqual "Window"
      product.id mustEqual inserted
    }

    "Single insert with inlined free variable" in {
      val prd = Product(0L, "test1", 1L)
      val (inserted, returnedProduct) =
        (for {
          i <- testContext.run {
            product.insert(_.sku -> lift(prd.sku), _.description -> lift(prd.description)).returningGenerated(_.id)
          }
          rps <- testContext.run(productById(lift(i)))
        } yield (i, rps.head)).runSyncUnsafe()

      returnedProduct.description mustEqual "test1"
      returnedProduct.sku mustEqual 1L
      returnedProduct.id mustEqual inserted
    }

    "Single insert with free variable and explicit quotation" in {
      val prd = Product(0L, "test2", 2L)
      val q1 = quote {
        product.insert(_.sku -> lift(prd.sku), _.description -> lift(prd.description)).returningGenerated(_.id)
      }
      val (inserted, returnedProduct) =
        (for {
          i <- testContext.run(q1)
          rps <- testContext.run(productById(lift(i)))
        } yield (i, rps.head)).runSyncUnsafe()

      returnedProduct.description mustEqual "test2"
      returnedProduct.sku mustEqual 2L
      returnedProduct.id mustEqual inserted
    }

    "Single product insert with a method quotation" in {
      val prd = Product(0L, "test3", 3L)
      val (inserted, returnedProduct) =
        (for {
          i <- testContext.run(productInsert(lift(prd)))
          rps <- testContext.run(productById(lift(i)))
        } yield (i, rps.head)).runSyncUnsafe()

      returnedProduct.description mustEqual "test3"
      returnedProduct.sku mustEqual 3L
      returnedProduct.id mustEqual inserted
    }
  }
} 
Example 119
Source File: MonixJdbcContextSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.h2

import io.getquill.MonixSpec
import monix.eval.Task

class MonixJdbcContextSpec extends MonixSpec {

  val context = testContext
  import testContext._

  "provides transaction support" - {
    "success" in {
      (for {
        _ <- testContext.run(qr1.delete)
        _ <- testContext.transaction {
          testContext.run(qr1.insert(_.i -> 33))
        }
        r <- testContext.run(qr1)
      } yield r).runSyncUnsafe().map(_.i) mustEqual List(33)
    }
    "success - stream" in {
      (for {
        _ <- testContext.run(qr1.delete)
        seq <- testContext.transaction {
          for {
            _ <- testContext.run(qr1.insert(_.i -> 33))
            s <- accumulate(testContext.stream(qr1))
          } yield s
        }
        r <- testContext.run(qr1)
      } yield (seq.map(_.i), r.map(_.i))).runSyncUnsafe() mustEqual ((List(33), List(33)))
    }
    "failure" in {
      (for {
        _ <- testContext.run(qr1.delete)
        e <- testContext.transaction {
          Task.sequence(Seq(
            testContext.run(qr1.insert(_.i -> 18)),
            Task.eval {
              throw new IllegalStateException
            }
          ))
        }.onErrorHandleWith {
          case e: Exception => Task(e.getClass.getSimpleName)
        }
        r <- testContext.run(qr1)
      } yield (e, r.isEmpty)).runSyncUnsafe() mustEqual (("IllegalStateException", true))
    }
    "nested" in {
      (for {
        _ <- testContext.run(qr1.delete)
        _ <- testContext.transaction {
          testContext.transaction {
            testContext.run(qr1.insert(_.i -> 33))
          }
        }
        r <- testContext.run(qr1)
      } yield r).runSyncUnsafe().map(_.i) mustEqual List(33)
    }
    "prepare" in {
      testContext.prepareParams(
        "select * from Person where name=? and age > ?", ps => (List("Sarah", 127), ps)
      ).runSyncUnsafe() mustEqual List("127", "'Sarah'")
    }
  }
} 
Example 120
Source File: ResultSetIteratorSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill

import io.getquill.context.monix.Runner
import io.getquill.util.LoadConfig
import monix.eval.Task
import monix.execution.Scheduler
import org.scalatest.BeforeAndAfterAll
import org.scalatest.freespec.AnyFreeSpec
import org.scalatest.matchers.must.Matchers
import org.scalatest.matchers.should.Matchers._

import scala.collection.mutable.ArrayBuffer

class ResultSetIteratorSpec extends AnyFreeSpec with Matchers with BeforeAndAfterAll {

  val ds = JdbcContextConfig(LoadConfig("testPostgresDB")).dataSource
  implicit val scheduler = Scheduler.global

  val ctx = new PostgresMonixJdbcContext(Literal, ds, Runner.default)
  import ctx._

  case class Person(name: String, age: Int)

  val peopleInsert =
    quote((p: Person) => query[Person].insert(p))

  val peopleEntries = List(
    Person("Alex", 60),
    Person("Bert", 55),
    Person("Cora", 33)
  )

  override def beforeAll = {
    ctx.transaction {
      for {
        _ <- ctx.run(query[Person].delete)
        _ <- ctx.run(liftQuery(peopleEntries).foreach(p => peopleInsert(p)))
      } yield ()
    }.runSyncUnsafe()
  }

  "traverses correctly" in {
    val results =
      Task(ds.getConnection).bracket { conn =>
        Task {
          val stmt = conn.prepareStatement("select * from person")
          val rs = new ResultSetIterator[String](stmt.executeQuery(), extractor = (rs) => { rs.getString(1) })
          val accum = ArrayBuffer[String]()
          while (rs.hasNext) accum += rs.next()
          accum
        }
      } { conn => Task(conn.close()) }.runSyncUnsafe()

    results should contain theSameElementsAs (peopleEntries.map(_.name))
  }

  "can take head element" in {
    val result =
      Task(ds.getConnection).bracket { conn =>
        Task {
          val stmt = conn.prepareStatement("select * from person where name = 'Alex'")
          val rs = new ResultSetIterator(stmt.executeQuery(), extractor = (rs) => { rs.getString(1) })
          rs.head
        }
      } { conn => Task(conn.close()) }.runSyncUnsafe()

    result must equal("Alex")
  }
} 
Example 121
Source File: PermissionsRoutes.scala    From nexus-iam   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.iam.routes

import akka.http.javadsl.server.Rejections._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import ch.epfl.bluebrain.nexus.iam.config.AppConfig.HttpConfig
import ch.epfl.bluebrain.nexus.iam.directives.AuthDirectives.authenticator
import ch.epfl.bluebrain.nexus.iam.marshallers.instances._
import ch.epfl.bluebrain.nexus.iam.permissions.Permissions
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.routes.PermissionsRoutes.PatchPermissions
import ch.epfl.bluebrain.nexus.iam.routes.PermissionsRoutes.PatchPermissions.{Append, Replace, Subtract}
import ch.epfl.bluebrain.nexus.iam.types.ResourceF._
import ch.epfl.bluebrain.nexus.iam.types.{Caller, Permission}
import io.circe.{Decoder, DecodingFailure}
import kamon.instrumentation.akka.http.TracingDirectives.operationName
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global


class PermissionsRoutes(permissions: Permissions[Task], realms: Realms[Task])(implicit http: HttpConfig) {

  def routes: Route =
    (pathPrefix("permissions") & pathEndOrSingleSlash) {
      operationName(s"/${http.prefix}/permissions") {
        authenticateOAuth2Async("*", authenticator(realms)).withAnonymousUser(Caller.anonymous) { implicit caller =>
          concat(
            get {
              parameter("rev".as[Long].?) {
                case Some(rev) => complete(permissions.fetchAt(rev).runNotFound)
                case None      => complete(permissions.fetch.runToFuture)
              }
            },
            (put & parameter("rev" ? 0L)) { rev =>
              entity(as[PatchPermissions]) {
                case Replace(set) =>
                  complete(permissions.replace(set, rev).runToFuture)
                case _ => reject(validationRejection("Only @type 'Replace' is permitted when using 'put'."))
              }
            },
            delete {
              parameter("rev".as[Long]) { rev =>
                complete(permissions.delete(rev).runToFuture)
              }
            },
            (patch & parameter("rev" ? 0L)) { rev =>
              entity(as[PatchPermissions]) {
                case Append(set) =>
                  complete(permissions.append(set, rev).runToFuture)
                case Subtract(set) =>
                  complete(permissions.subtract(set, rev).runToFuture)
                case _ =>
                  reject(validationRejection("Only @type 'Append' or 'Subtract' is permitted when using 'patch'."))
              }
            }
          )
        }
      }
    }
}

object PermissionsRoutes {

  private[routes] sealed trait PatchPermissions extends Product with Serializable

  private[routes] object PatchPermissions {

    final case class Append(permissions: Set[Permission])   extends PatchPermissions
    final case class Subtract(permissions: Set[Permission]) extends PatchPermissions
    final case class Replace(permissions: Set[Permission])  extends PatchPermissions

    implicit val patchPermissionsDecoder: Decoder[PatchPermissions] =
      Decoder.instance { hc =>
        for {
          permissions <- hc.get[Set[Permission]]("permissions")
          tpe = hc.get[String]("@type").getOrElse("Replace")
          patch <- tpe match {
            case "Replace"  => Right(Replace(permissions))
            case "Append"   => Right(Append(permissions))
            case "Subtract" => Right(Subtract(permissions))
            case _          => Left(DecodingFailure("@type field must have Append or Subtract value", hc.history))
          }
        } yield patch
      }
  }

} 
Example 122
Source File: IdentitiesRoutes.scala    From nexus-iam   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.iam.routes

import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import ch.epfl.bluebrain.nexus.iam.config.AppConfig.HttpConfig
import ch.epfl.bluebrain.nexus.iam.directives.AuthDirectives.authenticator
import ch.epfl.bluebrain.nexus.iam.marshallers.instances._
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.types.Caller
import ch.epfl.bluebrain.nexus.iam.types.Caller.JsonLd._
import kamon.instrumentation.akka.http.TracingDirectives.operationName
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global


class IdentitiesRoutes(realms: Realms[Task])(implicit http: HttpConfig) {

  def routes: Route = {
    (pathPrefix("identities") & pathEndOrSingleSlash) {
      operationName(s"/${http.prefix}/identities") {
        authenticateOAuth2Async("*", authenticator(realms)).withAnonymousUser(Caller.anonymous) { implicit caller =>
          get {
            complete(caller)
          }
        }
      }
    }
  }
} 
Example 123
Source File: AuthDirectives.scala    From nexus-iam   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.iam.directives

import akka.http.scaladsl.model.headers.OAuth2BearerToken
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.directives.Credentials
import akka.http.scaladsl.server.{Directive0, Directive1}
import cats.implicits._
import ch.epfl.bluebrain.nexus.rdf.implicits._
import ch.epfl.bluebrain.nexus.iam.acls.Acls
import ch.epfl.bluebrain.nexus.iam.auth.AccessToken
import ch.epfl.bluebrain.nexus.iam.config.AppConfig.HttpConfig
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.types.IamError.AccessDenied
import ch.epfl.bluebrain.nexus.iam.types.{Caller, Permission}
import ch.epfl.bluebrain.nexus.rdf.Iri.{AbsoluteIri, Path}
import monix.eval.Task
import monix.execution.Scheduler

import scala.concurrent.Future

object AuthDirectives {

  
  def authorizeFor(permission: Permission)(
      implicit
      acls: Acls[Task],
      s: Scheduler,
      c: Caller,
      hc: HttpConfig
  ): Directive0 =
    extractResourceAddress.flatMap { address =>
      onSuccess {
        acls
          .hasPermission(Path./, permission, ancestors = false)
          .ifM(Task.unit, Task.raiseError(AccessDenied(address, permission)))
          .runToFuture
      }
    }
} 
Example 124
Source File: instances.scala    From nexus-iam   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.iam.marshallers

import akka.http.scaladsl.marshalling.GenericMarshallers.eitherMarshaller
import akka.http.scaladsl.marshalling._
import akka.http.scaladsl.model.MediaTypes.`application/json`
import akka.http.scaladsl.model._
import ch.epfl.bluebrain.nexus.commons.circe.syntax._
import ch.epfl.bluebrain.nexus.commons.http.JsonLdCirceSupport.OrderedKeys
import ch.epfl.bluebrain.nexus.commons.http.RdfMediaTypes._
import ch.epfl.bluebrain.nexus.commons.http.directives.StatusFrom
import ch.epfl.bluebrain.nexus.iam.acls.AclRejection
import ch.epfl.bluebrain.nexus.iam.config.AppConfig._
import ch.epfl.bluebrain.nexus.iam.permissions.PermissionsRejection
import ch.epfl.bluebrain.nexus.iam.realms.RealmRejection
import ch.epfl.bluebrain.nexus.iam.types.IamError.InternalError
import ch.epfl.bluebrain.nexus.iam.types.{IamError, ResourceRejection}
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport
import io.circe._
import io.circe.syntax._
import monix.eval.Task
import monix.execution.Scheduler

import scala.collection.immutable.Seq
import scala.concurrent.Future
import scala.concurrent.duration.FiniteDuration

object instances extends FailFastCirceSupport {

  implicit val finiteDurationEncoder: Encoder[FiniteDuration] =
    Encoder.encodeString.contramap(fd => s"${fd.toMillis} ms")

  implicit val resourceRejectionEncoder: Encoder[ResourceRejection] =
    Encoder.instance {
      case r: AclRejection         => Encoder[AclRejection].apply(r)
      case r: RealmRejection       => Encoder[RealmRejection].apply(r)
      case r: PermissionsRejection => Encoder[PermissionsRejection].apply(r)
      case _                       => Encoder[IamError].apply(InternalError("unspecified"))
    }

  implicit val resourceRejectionStatusFrom: StatusFrom[ResourceRejection] =
    StatusFrom {
      case r: AclRejection         => AclRejection.aclRejectionStatusFrom(r)
      case r: RealmRejection       => RealmRejection.realmRejectionStatusFrom(r)
      case r: PermissionsRejection => PermissionsRejection.permissionsRejectionStatusFrom(r)
    }

  override def unmarshallerContentTypes: Seq[ContentTypeRange] =
    List(`application/json`, `application/ld+json`, `application/sparql-results+json`)

  
  implicit final def rejection[A <: ResourceRejection: Encoder](
      implicit statusFrom: StatusFrom[A],
      printer: Printer = Printer.noSpaces.copy(dropNullValues = true),
      ordered: OrderedKeys = orderedKeys
  ): ToResponseMarshaller[A] = {
    val marshallers = Seq(`application/ld+json`, `application/json`).map { contentType =>
      Marshaller.withFixedContentType[A, HttpResponse](contentType) { rejection =>
        HttpResponse(
          status = statusFrom(rejection),
          entity = HttpEntity(contentType, printer.print(rejection.asJson.sortKeys))
        )
      }
    }
    Marshaller.oneOf(marshallers: _*)
  }

  implicit class EitherTask[R <: ResourceRejection, A](task: Task[Either[R, A]])(implicit s: Scheduler) {
    def runWithStatus(code: StatusCode): Future[Either[R, (StatusCode, A)]] =
      task.map(_.map(code -> _)).runToFuture
  }

  implicit class OptionTask[A](task: Task[Option[A]])(implicit s: Scheduler) {
    def runNotFound: Future[A] =
      task.flatMap {
        case Some(a) => Task.pure(a)
        case None    => Task.raiseError(IamError.NotFound)
      }.runToFuture
  }
} 
Example 125
Source File: RepairFromMessages.scala    From nexus-iam   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.iam

import java.net.URLDecoder

import akka.actor.ActorSystem
import akka.persistence.cassandra.query.scaladsl.CassandraReadJournal
import akka.persistence.query.PersistenceQuery
import ch.epfl.bluebrain.nexus.iam.acls.Acls
import ch.epfl.bluebrain.nexus.iam.permissions.Permissions
import ch.epfl.bluebrain.nexus.iam.realms.Realms
import ch.epfl.bluebrain.nexus.iam.types.Label
import ch.epfl.bluebrain.nexus.rdf.Iri.Path
import com.typesafe.scalalogging.Logger
import monix.eval.Task
import monix.execution.Scheduler
import monix.execution.schedulers.CanBlock

import scala.concurrent.Future


object RepairFromMessages {
  // $COVERAGE-OFF$

  private val log = Logger[RepairFromMessages.type]

  def repair(
      p: Permissions[Task],
      r: Realms[Task],
      a: Acls[Task]
  )(implicit as: ActorSystem, sc: Scheduler, pm: CanBlock): Unit = {
    val pq = PersistenceQuery(as).readJournalFor[CassandraReadJournal](CassandraReadJournal.Identifier)

    pq.currentPersistenceIds()
      .mapAsync(1) {
        case PermissionsId() => p.agg.currentState(p.persistenceId).runToFuture
        case RealmId(label)  => r.agg.currentState(label.value).runToFuture
        case AclId(path)     => a.agg.currentState(path.asString).runToFuture
        case other =>
          log.warn(s"Unknown persistence id '$other'")
          Future.successful(())
      }
      .runFold(0) {
        case (acc, _) =>
          if (acc % 100 == 0) log.info(s"Processed '$acc' persistence ids.")
          acc + 1
      }
      .runSyncDiscard()

    log.info("Repair from messages table completed.")
  }

  sealed abstract class PersistenceId(prefix: String) {
    private val len = prefix.length
    protected def dropPrefix(arg: String): Option[String] =
      if (arg.startsWith(prefix)) Some(arg.drop(len))
      else None
  }
  object RealmId extends PersistenceId("realms-") {
    def unapply(arg: String): Option[Label] =
      dropPrefix(arg).map(Label.unsafe)
  }
  object AclId extends PersistenceId("acls-") {
    def unapply(arg: String): Option[Path] =
      dropPrefix(arg).flatMap(str => Path(URLDecoder.decode(str, "UTF-8")).toOption)
  }
  object PermissionsId {
    def unapply(arg: String): Boolean =
      arg == "permissions-permissions"
  }

  implicit class RichFuture[A](val future: Future[A]) extends AnyVal {
    def runSyncDiscard()(implicit s: Scheduler, permit: CanBlock): Unit =
      Task.fromFuture(future).map(_ => ()).runSyncUnsafe()
  }
  // $COVERAGE-ON$
} 
Example 126
Source File: IdentitiesRoutesSpec.scala    From nexus-iam   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.iam.routes

import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.model.headers.OAuth2BearerToken
import akka.http.scaladsl.testkit.ScalatestRouteTest
import ch.epfl.bluebrain.nexus.commons.test.Resources
import ch.epfl.bluebrain.nexus.iam.auth.{AccessToken, TokenRejection}
import ch.epfl.bluebrain.nexus.iam.config.{AppConfig, Settings}
import ch.epfl.bluebrain.nexus.iam.marshallers.instances._
import ch.epfl.bluebrain.nexus.iam.realms._
import ch.epfl.bluebrain.nexus.iam.testsyntax._
import ch.epfl.bluebrain.nexus.iam.types.Caller
import ch.epfl.bluebrain.nexus.iam.types.IamError.InvalidAccessToken
import ch.epfl.bluebrain.nexus.iam.types.Identity.{Anonymous, Authenticated, User}
import com.typesafe.config.{Config, ConfigFactory}
import io.circe.Json
import monix.eval.Task
import org.mockito.matchers.MacroBasedMatchers
import org.mockito.{IdiomaticMockito, Mockito}
import org.scalatest.BeforeAndAfter
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike

import scala.concurrent.duration._

//noinspection TypeAnnotation
class IdentitiesRoutesSpec
    extends AnyWordSpecLike
    with Matchers
    with ScalatestRouteTest
    with BeforeAndAfter
    with MacroBasedMatchers
    with Resources
    with ScalaFutures
    with IdiomaticMockito {

  override implicit def patienceConfig: PatienceConfig = PatienceConfig(3.seconds, 100.milliseconds)

  override def testConfig: Config = ConfigFactory.load("test.conf")

  private val appConfig: AppConfig = Settings(system).appConfig
  private implicit val http        = appConfig.http

  private val realms: Realms[Task] = mock[Realms[Task]]

  before {
    Mockito.reset(realms)
  }

  "The IdentitiesRoutes" should {
    val routes = Routes.wrap(new IdentitiesRoutes(realms).routes)
    "return forbidden" in {
      val err = InvalidAccessToken(TokenRejection.InvalidAccessToken)
      realms.caller(any[AccessToken]) shouldReturn Task.raiseError(err)
      Get("/identities").addCredentials(OAuth2BearerToken("token")) ~> routes ~> check {
        status shouldEqual StatusCodes.Unauthorized
      }
    }
    "return anonymous" in {
      realms.caller(any[AccessToken]) shouldReturn Task.pure(Caller.anonymous)
      Get("/identities") ~> routes ~> check {
        status shouldEqual StatusCodes.OK
        responseAs[Json].sort shouldEqual jsonContentOf("/identities/anonymous.json")
      }
    }
    "return all identities" in {
      val user   = User("theuser", "therealm")
      val auth   = Authenticated("therealm")
      val caller = Caller(user, Set(user, Anonymous, auth))
      realms.caller(any[AccessToken]) shouldReturn Task.pure(caller)
      Get("/identities").addCredentials(OAuth2BearerToken("token")) ~> routes ~> check {
        status shouldEqual StatusCodes.OK
        responseAs[Json].sort shouldEqual jsonContentOf("/identities/identities.json")
      }
    }
  }
} 
Example 127
Source File: HttpMockServerResource.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.http.server

import com.github.agourlay.cornichon.core.{ RunState, Session }
import com.github.agourlay.cornichon.dsl.BlockScopedResource
import com.github.agourlay.cornichon.http.server.HttpMockServerResource.SessionKeys._
import io.circe.Json
import monix.eval.Task
import monix.execution.Scheduler

case class HttpMockServerResource(interface: Option[String], label: String, portRange: Option[Range])
  extends BlockScopedResource {

  implicit val scheduler = Scheduler.Implicits.global

  val sessionTarget: String = label
  val openingTitle: String = s"Starting HTTP mock server '$label'"
  val closingTitle: String = s"Shutting down HTTP mock server '$label'"

  def use[A](outsideRunState: RunState)(f: RunState => Task[A]): Task[(Session, A)] = {
    val mockRequestHandler = new MockServerRequestHandler()

    val initSession: String => Session = id => Session.newEmpty.addValueUnsafe(s"$label-url", id)
    val resourceContext: RunState => Session => RunState = r1 => s1 => r1.mergeSessions(s1)
    val runWithServer = initSession.andThen(resourceContext(outsideRunState)).andThen(f)

    val mockServer = new MockHttpServer(interface, portRange, mockRequestHandler.mockService)(runWithServer)

    mockServer.useServer().map { res =>
      val resourceResults = requestsResults(mockRequestHandler)
      (resourceResults, res)
    }
  }

  def requestsResults(mockRequestHandler: MockServerRequestHandler): Session = {
    val jsonRequests = mockRequestHandler.fetchRecordedRequestsAsJson()
    Session.newEmpty
      .addValueUnsafe(s"$sessionTarget$receivedBodiesSuffix", Json.fromValues(jsonRequests).spaces2)
      .addValueUnsafe(s"$sessionTarget$nbReceivedCallsSuffix", jsonRequests.size.toString)
  }
}

object HttpMockServerResource {
  object SessionKeys {
    val nbReceivedCallsSuffix = "-nb-received-calls"
    val receivedBodiesSuffix = "-received-bodies"
  }
} 
Example 128
Source File: TurnstileAPI.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.framework.examples.propertyCheck.turnstile

import com.github.agourlay.cornichon.framework.examples.HttpServer
import monix.eval.Task
import monix.execution.atomic.AtomicBoolean
import monix.execution.{ CancelableFuture, Scheduler }
import org.http4s._
import org.http4s.implicits._
import org.http4s.dsl._
import org.http4s.server.Router
import org.http4s.server.blaze.BlazeServerBuilder

class TurnstileAPI extends Http4sDsl[Task] {

  implicit val s = Scheduler.Implicits.global

  private val turnstileLocked = AtomicBoolean(true)

  private val turnstileService = HttpRoutes.of[Task] {
    case POST -> Root / "push-coin" =>
      if (turnstileLocked.get()) {
        turnstileLocked.set(false)
        Ok("payment accepted")
      } else
        BadRequest("payment refused")

    case POST -> Root / "walk-through" =>
      if (turnstileLocked.get())
        BadRequest("door blocked")
      else {
        turnstileLocked.set(true)
        Ok("door turns")
      }
  }

  private val routes = Router(
    "/" -> turnstileService
  )

  def start(httpPort: Int): CancelableFuture[HttpServer] =
    BlazeServerBuilder[Task](executionContext = s)
      .bindHttp(httpPort, "localhost")
      .withoutBanner
      .withNio2(true)
      .withHttpApp(routes.orNotFound)
      .allocated
      .map { case (_, stop) => new HttpServer(stop) }
      .runToFuture

} 
Example 129
Source File: ReverseAPI.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.framework.examples.propertyCheck.stringReverse

import com.github.agourlay.cornichon.framework.examples.HttpServer
import monix.eval.Task
import monix.execution.{ CancelableFuture, Scheduler }
import org.http4s._
import org.http4s.implicits._
import org.http4s.dsl._
import org.http4s.server.Router
import org.http4s.server.blaze.BlazeServerBuilder

class ReverseAPI extends Http4sDsl[Task] {

  implicit val s = Scheduler.Implicits.global

  object WordQueryParamMatcher extends QueryParamDecoderMatcher[String]("word")

  private val reverseService = HttpRoutes.of[Task] {
    case POST -> Root / "double-reverse" :? WordQueryParamMatcher(word) =>
      Ok(word.reverse.reverse)
  }

  private val routes = Router(
    "/" -> reverseService
  )

  def start(httpPort: Int): CancelableFuture[HttpServer] =
    BlazeServerBuilder[Task](executionContext = s)
      .bindHttp(httpPort, "localhost")
      .withoutBanner
      .withNio2(true)
      .withHttpApp(routes.orNotFound)
      .allocated
      .map { case (_, stop) => new HttpServer(stop) }
      .runToFuture
} 
Example 130
Source File: ForAllStep.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.steps.check

import cats.data.StateT
import com.github.agourlay.cornichon.core.Done.rightDone
import com.github.agourlay.cornichon.core.{ Generator, _ }
import monix.eval.Task

case class ForAllStep[A, B, C, D, E, F](description: String, maxNumberOfRuns: Int)(ga: RandomContext => Generator[A], gb: RandomContext => Generator[B], gc: RandomContext => Generator[C], gd: RandomContext => Generator[D], ge: RandomContext => Generator[E], gf: RandomContext => Generator[F])(f: A => B => C => D => E => F => Step) extends WrapperStep {

  val baseTitle = s"ForAll values of generators check '$description'"
  val title = s"$baseTitle with maxNumberOfRuns=$maxNumberOfRuns"

  override val stateUpdate: StepState = StateT { runState =>
    val randomContext = runState.randomContext
    val genA = ga(randomContext)
    val genB = gb(randomContext)
    val genC = gc(randomContext)
    val genD = gd(randomContext)
    val genE = ge(randomContext)
    val genF = gf(randomContext)

    def repeatEvaluationOnSuccess(runNumber: Int)(runState: RunState): Task[(RunState, Either[FailedStep, Done])] =
      if (runNumber > maxNumberOfRuns)
        Task.now((runState, rightDone))
      else {
        val s = runState.session
        val generatedA = genA.value(s)()
        val generatedB = genB.value(s)()
        val generatedC = genC.value(s)()
        val generatedD = genD.value(s)()
        val generatedE = genE.value(s)()
        val generatedF = genF.value(s)()

        val preRunLog = InfoLogInstruction(s"Run #$runNumber", runState.depth)
        val invariantRunState = runState.nestedContext.recordLog(preRunLog)
        val invariantStep = f(generatedA)(generatedB)(generatedC)(generatedD)(generatedE)(generatedF)

        invariantStep.runStep(invariantRunState).flatMap {
          case (newState, l @ Left(_)) =>
            val postRunLog = InfoLogInstruction(s"Run #$runNumber - Failed", runState.depth)
            val failedState = runState.mergeNested(newState).recordLog(postRunLog)
            Task.now((failedState, l))
          case (newState, _) =>
            val postRunLog = InfoLogInstruction(s"Run #$runNumber", runState.depth)
            // success case we are not propagating the Session so runs do not interfere with each-others
            val nextRunState = runState.recordLogStack(newState.logStack).recordLog(postRunLog).registerCleanupSteps(newState.cleanupSteps)
            repeatEvaluationOnSuccess(runNumber + 1)(nextRunState)
        }
      }

    repeatEvaluationOnSuccess(1)(runState.nestedContext)
      .timed
      .map {
        case (executionTime, (checkState, res)) =>
          val depth = runState.depth
          val exec = Some(executionTime)
          val fullLogs = res match {
            case Left(_) =>
              FailureLogInstruction(s"$baseTitle block failed ", depth, exec) +: checkState.logStack :+ failedTitleLog(depth)
            case _ =>
              SuccessLogInstruction(s"$baseTitle block succeeded", depth, exec) +: checkState.logStack :+ successTitleLog(depth)
          }
          (runState.mergeNested(checkState, fullLogs), res)
      }
  }
} 
Example 131
Source File: AssertStep.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.steps.regular.assertStep

import cats.data._
import cats.data.Validated.Invalid
import cats.syntax.either._
import com.github.agourlay.cornichon.core.ScenarioRunner._
import com.github.agourlay.cornichon.core._
import monix.eval.Task

import scala.concurrent.duration.Duration

case class AssertStep(title: String, action: ScenarioContext => Assertion, show: Boolean = true) extends LogValueStep[Done] {

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

  override def runLogValueStep(runState: RunState): Task[Either[NonEmptyList[CornichonError], Done]] =
    Task.delay {
      val assertion = action(runState.scenarioContext)
      assertion.validated match {
        case Invalid(e) => e.asLeft
        case _          => Done.rightDone
      }
    }

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

  override def logOnSuccess(result: Done, runState: RunState, executionTime: Duration): LogInstruction =
    successLog(title, runState.depth, show, executionTime)
} 
Example 132
Source File: EffectStep.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.steps.regular

import cats.data.{ EitherT, NonEmptyList }
import cats.syntax.either._
import com.github.agourlay.cornichon.core._
import com.github.agourlay.cornichon.core.ScenarioRunner._
import monix.eval.Task

import scala.concurrent.duration.Duration
import scala.concurrent.{ ExecutionContext, Future }

case class EffectStep(title: String, effect: ScenarioContext => Future[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.deferFuture(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(title: String, effect: ScenarioContext => EitherT[Future, CornichonError, Session], show: Boolean = true): EffectStep = {
    val effectT: ScenarioContext => Future[Either[CornichonError, Session]] = s => effect(s).value
    EffectStep(title, effectT, show)
  }

  def fromSync(title: String, effect: ScenarioContext => Session, show: Boolean = true): EffectStep = {
    val effectF: ScenarioContext => Future[Either[CornichonError, Session]] = s => Future.successful(effect(s).asRight)
    EffectStep(title, effectF, show)
  }

  def fromSyncE(title: String, effect: ScenarioContext => Either[CornichonError, Session], show: Boolean = true): EffectStep = {
    val effectF: ScenarioContext => Future[Either[CornichonError, Session]] = s => Future.successful(effect(s))
    EffectStep(title, effectF, show)
  }

  def fromAsync(title: String, effect: ScenarioContext => Future[Session], show: Boolean = true)(implicit ec: ExecutionContext): EffectStep = {
    val effectF: ScenarioContext => Future[Either[CornichonError, Session]] = s => effect(s).map(Right.apply)
    EffectStep(title, effectF, show)
  }
} 
Example 133
Source File: RepeatConcurrentlyStep.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.steps.wrapped

import cats.data.StateT
import cats.instances.list._
import cats.syntax.foldable._
import cats.syntax.either._
import com.github.agourlay.cornichon.core._
import com.github.agourlay.cornichon.core.Done._
import monix.eval.Task
import monix.reactive.Observable

import scala.concurrent.duration.FiniteDuration
import scala.util.control.NonFatal

case class RepeatConcurrentlyStep(times: Int, nested: List[Step], parallelism: Int, maxTime: FiniteDuration) extends WrapperStep {
  require(parallelism > 0, "repeat concurrently block must contain a positive 'parallelism' factor")
  require(times > 0, "repeat concurrently block must contain a positive 'times' factor")
  require(times >= parallelism, "repeat concurrently block must contain a 'parallelism' factor <= to the number of repeat 'times'")

  val title = s"Repeat concurrently block '$times' times with parallel factor '$parallelism' and maxTime '$maxTime'"

  override val stateUpdate: StepState = StateT { runState =>
    val nestedRunState = runState.nestedContext
    val initialDepth = runState.depth
    Observable.fromIterable(List.fill(times)(Done))
      .mapParallelUnordered(parallelism)(_ => ScenarioRunner.runStepsShortCircuiting(nested, nestedRunState))
      .takeUntil(Observable.evalDelayed(maxTime, Done))
      .toListL
      .timed
      .flatMap {
        case (executionTime, results) =>
          if (results.size != times) {
            val error = RepeatConcurrentlyTimeout(times, results.size)
            val errorState = runState.recordLog(failedTitleLog(initialDepth)).recordLog(FailureLogInstruction(error.renderedMessage, initialDepth, Some(executionTime)))
            val failedStep = FailedStep.fromSingle(this, error)
            Task.now(errorState -> failedStep.asLeft)
          } else {
            val failedStepRuns = results.collect { case (s, r @ Left(_)) => (s, r) }
            failedStepRuns.headOption.fold[Task[(RunState, Either[FailedStep, Done])]] {
              val successStepsRun = results.collect { case (s, r @ Right(_)) => (s, r) }
              val allRunStates = successStepsRun.map(_._1)
              //TODO all logs should be merged?
              // all runs were successful, we pick the first one for the logs
              val firstStateLog = allRunStates.head.logStack
              val wrappedLogStack = SuccessLogInstruction(s"Repeat concurrently block succeeded", initialDepth, Some(executionTime)) +: firstStateLog :+ successTitleLog(initialDepth)
              // TODO merge all sessions together - require diffing Sessions otherwise it produces a huge map full of duplicate as they all started from the same.
              val updatedSession = allRunStates.head.session
              // merge all cleanups steps
              val allCleanupSteps = allRunStates.foldMap(_.cleanupSteps)
              val successState = runState.withSession(updatedSession).recordLogStack(wrappedLogStack).registerCleanupSteps(allCleanupSteps)
              Task.now(successState -> rightDone)
            } {
              case (s, failedStep) =>
                val ratio = s"'${failedStepRuns.size}/$times' run(s)"
                val wrapLogStack = FailureLogInstruction(s"Repeat concurrently block failed for $ratio", initialDepth) +: s.logStack :+ failedTitleLog(initialDepth)
                Task.now(runState.mergeNested(s, wrapLogStack) -> failedStep)
            }
          }
      }.onErrorRecover {
        case NonFatal(e) =>
          val failedStep = FailedStep.fromSingle(this, RepeatConcurrentlyError(e))
          (runState.recordLog(failedTitleLog(initialDepth)), failedStep.asLeft)
      }
  }
}

case class RepeatConcurrentlyTimeout(times: Int, success: Int) extends CornichonError {
  lazy val baseErrorMessage = s"Repeat concurrently block did not reach completion in time: $success/$times finished"
}

case class RepeatConcurrentlyError(cause: Throwable) extends CornichonError {
  lazy val baseErrorMessage = "Repeat concurrently block has thrown an error"
  override val causedBy = CornichonError.fromThrowable(cause) :: Nil
} 
Example 134
Source File: ConcurrentlyStep.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.steps.wrapped

import cats.data.StateT
import cats.instances.list._
import cats.syntax.foldable._
import com.github.agourlay.cornichon.core._
import com.github.agourlay.cornichon.core.Done._
import monix.eval.Task
import monix.reactive.Observable

import scala.concurrent.duration.FiniteDuration
import scala.util.control.NonFatal

case class ConcurrentlyStep(nested: List[Step], maxTime: FiniteDuration) extends WrapperStep {

  val title = s"Concurrently block with maxTime '$maxTime'"

  override val stateUpdate: StepState = StateT { runState =>
    val nestedRunState = runState.nestedContext
    val initialDepth = runState.depth
    Observable.fromIterable(nested)
      .mapParallelUnordered(nested.size)(s => ScenarioRunner.runStepsShortCircuiting(s :: Nil, nestedRunState))
      .takeUntil(Observable.evalDelayed(maxTime, ()))
      .toListL
      .timed
      .flatMap {
        case (executionTime, results) =>
          if (results.size != nested.size) {
            val error = ConcurrentlyTimeout(nested.size, results.size)
            val errorState = runState.recordLog(failedTitleLog(initialDepth)).recordLog(FailureLogInstruction(error.renderedMessage, initialDepth, Some(executionTime)))
            val failedStep = FailedStep.fromSingle(this, error)
            Task.now(errorState -> Left(failedStep))
          } else {
            val failedStepRuns = results.collect { case (s, r @ Left(_)) => (s, r) }
            failedStepRuns.headOption.fold[Task[(RunState, Either[FailedStep, Done])]] {
              val successStepsRun = results.collect { case (s, r @ Right(_)) => (s, r) }
              val allRunStates = successStepsRun.map(_._1)
              //TODO all logs should be merged?
              // all runs were successful, we pick the first one for the logs
              val firstStateLog = allRunStates.head.logStack
              val wrappedLogStack = SuccessLogInstruction(s"Concurrently block succeeded", initialDepth, Some(executionTime)) +: firstStateLog :+ successTitleLog(initialDepth)
              // TODO merge all sessions together - require diffing Sessions or it produces a huge map full of duplicate as they all started from the same.
              val updatedSession = allRunStates.head.session
              // merge all cleanups steps
              val allCleanupSteps = allRunStates.foldMap(_.cleanupSteps)
              val successState = runState.withSession(updatedSession).recordLogStack(wrappedLogStack).registerCleanupSteps(allCleanupSteps)
              Task.now((successState, rightDone))
            } {
              case (s, failedStep) =>
                val ratio = s"'${failedStepRuns.size}/${nested.size}' run(s)"
                val wrapLogStack = FailureLogInstruction(s"Concurrently block failed for $ratio", initialDepth) +: s.logStack :+ failedTitleLog(initialDepth)
                Task.now((runState.mergeNested(s, wrapLogStack), failedStep))
            }
          }
      }.onErrorRecover {
        case NonFatal(e) =>
          val failedStep = FailedStep.fromSingle(this, ConcurrentlyError(e))
          (runState.recordLog(failedTitleLog(initialDepth)), Left(failedStep))
      }
  }
}

case class ConcurrentlyTimeout(total: Int, success: Int) extends CornichonError {
  lazy val baseErrorMessage = s"Concurrently block did not reach completion in time: $success/$total finished"
}

case class ConcurrentlyError(cause: Throwable) extends CornichonError {
  lazy val baseErrorMessage = "Concurrently block has thrown an error"
  override val causedBy = CornichonError.fromThrowable(cause) :: Nil
} 
Example 135
Source File: FlatMapStep.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.steps.wrapped

import cats.data.StateT
import com.github.agourlay.cornichon.core._
import monix.eval.Task

// Transparent wrapper - Steps are flatten in the main execution
case class FlatMapStep(started: Step, nestedProducers: Session => List[Step]) extends WrapperStep {

  val title: String = ""

  override val stateUpdate: StepState = StateT { runState =>
    ScenarioRunner.runStepsShortCircuiting(started :: Nil, runState).flatMap {
      case t @ (rs2, res) =>
        if (res.isLeft)
          Task.now(t)
        else {
          val nestedStep = nestedProducers(rs2.session)
          ScenarioRunner.runStepsShortCircuiting(nestedStep, rs2)
        }
    }
  }

} 
Example 136
Source File: RetryMaxStep.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.steps.wrapped

import cats.data.{ NonEmptyList, StateT }
import com.github.agourlay.cornichon.core._
import com.github.agourlay.cornichon.core.Done._
import monix.eval.Task

case class RetryMaxStep(nested: List[Step], limit: Int) extends WrapperStep {

  require(limit > 0, "retry max limit must be a positive number")

  val title = s"RetryMax block with limit '$limit'"

  override val stateUpdate: StepState = StateT { runState =>

    def retryMaxSteps(runState: RunState, limit: Int, retriesNumber: Long): Task[(Long, RunState, Either[FailedStep, Done])] =
      ScenarioRunner.runStepsShortCircuiting(nested, runState.resetLogStack).flatMap {
        case (retriedState, Left(_)) if limit > 0 =>
          // In case of success all logs are returned but they are not printed by default.
          retryMaxSteps(runState.recordLogStack(retriedState.logStack), limit - 1, retriesNumber + 1)

        case (retriedState, l @ Left(_)) =>
          // In case of failure only the logs of the last run are shown to avoid giant traces.
          Task.now((retriesNumber, retriedState, l))

        case (retriedState, _) =>
          val successState = runState.withSession(retriedState.session).recordLogStack(retriedState.logStack)
          Task.now((retriesNumber, successState, rightDone))

      }

    retryMaxSteps(runState.nestedContext, limit, 0)
      .timed
      .map {
        case (executionTime, run) =>
          val (retries, retriedState, report) = run
          val depth = runState.depth
          val (logStack, res) = report.fold(
            failedStep => {
              val wrappedLogStack = FailureLogInstruction(s"RetryMax block with limit '$limit' failed", depth, Some(executionTime)) +: retriedState.logStack :+ failedTitleLog(depth)
              val artificialFailedStep = FailedStep.fromSingle(failedStep.step, RetryMaxBlockReachedLimit(limit, failedStep.errors))
              (wrappedLogStack, Left(artificialFailedStep))
            },
            _ => {
              val wrappedLogStack = SuccessLogInstruction(s"RetryMax block with limit '$limit' succeeded after '$retries' retries", depth, Some(executionTime)) +: retriedState.logStack :+ successTitleLog(depth)
              (wrappedLogStack, rightDone)
            }
          )
          (runState.mergeNested(retriedState, logStack), res)
      }
  }
}

case class RetryMaxBlockReachedLimit(limit: Int, errors: NonEmptyList[CornichonError]) extends CornichonError {
  lazy val baseErrorMessage = s"Retry max block failed '$limit' times"
  override val causedBy = errors.toList
} 
Example 137
Source File: RepeatWithStep.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.steps.wrapped

import cats.data.{ NonEmptyList, StateT }
import com.github.agourlay.cornichon.core.Done.rightDone
import com.github.agourlay.cornichon.core._
import monix.eval.Task

case class RepeatWithStep(nested: List[Step], elements: List[String], elementName: String) extends WrapperStep {

  require(elements.nonEmpty, "repeatWith block must contain a non empty sequence of elements")

  val printElements = s"[${elements.mkString(", ")}]"
  val title = s"RepeatWith block with elements $printElements"

  override val stateUpdate: StepState = StateT { runState =>

    def repeatSuccessSteps(remainingElements: List[String], runState: RunState): Task[(RunState, Either[(String, FailedStep), Done])] =
      remainingElements match {
        case Nil =>
          Task.now((runState, rightDone))
        case element :: tail =>
          // reset logs at each loop to have the possibility to not aggregate in failure case
          val rs = runState.resetLogStack
          val runStateWithIndex = rs.addToSession(elementName, element)
          ScenarioRunner.runStepsShortCircuiting(nested, runStateWithIndex).flatMap {
            case (onceMoreRunState, stepResult) =>
              stepResult.fold(
                failed => {
                  // In case of failure only the logs of the last run are shown to avoid giant traces.
                  Task.now((onceMoreRunState, Left((element, failed))))
                },
                _ => {
                  val successState = runState.mergeNested(onceMoreRunState)
                  repeatSuccessSteps(tail, successState)
                }
              )
          }
      }

    repeatSuccessSteps(elements, runState.nestedContext).timed
      .map {
        case (executionTime, (repeatedState, report)) =>
          val depth = runState.depth
          val (logStack, res) = report match {
            case Right(_) =>
              val wrappedLogStack = SuccessLogInstruction(s"RepeatWith block with elements $printElements succeeded", depth, Some(executionTime)) +: repeatedState.logStack :+ successTitleLog(depth)
              (wrappedLogStack, rightDone)
            case Left((failedElement, failedStep)) =>
              val wrappedLogStack = FailureLogInstruction(s"RepeatWith block with elements $printElements failed at element '$failedElement'", depth, Some(executionTime)) +: repeatedState.logStack :+ failedTitleLog(depth)
              val artificialFailedStep = FailedStep.fromSingle(failedStep.step, RepeatWithBlockContainFailedSteps(failedElement, failedStep.errors))
              (wrappedLogStack, Left(artificialFailedStep))
          }
          (runState.mergeNested(repeatedState, logStack), res)
      }
  }
}

case class RepeatWithBlockContainFailedSteps(element: String, errors: NonEmptyList[CornichonError]) extends CornichonError {
  lazy val baseErrorMessage = s"RepeatWith block failed for element '$element'"
  override val causedBy = errors.toList
} 
Example 138
Source File: WithDataInputStep.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.steps.wrapped

import cats.data.{ NonEmptyList, StateT }
import com.github.agourlay.cornichon.core._
import com.github.agourlay.cornichon.json.CornichonJson
import com.github.agourlay.cornichon.core.ScenarioRunner._
import com.github.agourlay.cornichon.core.Done._
import com.github.agourlay.cornichon.util.Printing._
import monix.eval.Task

case class WithDataInputStep(nested: List[Step], where: String) extends WrapperStep {

  val title = s"With data input block $where"

  override val stateUpdate: StepState = StateT { runState =>

    def runInputs(inputs: List[List[(String, String)]], runState: RunState): Task[(RunState, Either[(List[(String, String)], FailedStep), Done])] =
      if (inputs.isEmpty) Task.now((runState, rightDone))
      else {
        val currentInputs = inputs.head
        val runInfo = InfoLogInstruction(s"Run with inputs ${printArrowPairs(currentInputs)}", runState.depth)
        val bootstrapFilledInput = runState.addToSession(currentInputs).withLog(runInfo).goDeeper
        ScenarioRunner.runStepsShortCircuiting(nested, bootstrapFilledInput).flatMap {
          case (filledState, stepsResult) =>
            stepsResult.fold(
              failedStep => {
                // Prepend previous logs
                Task.now((runState.mergeNested(filledState), Left((currentInputs, failedStep))))
              },
              _ => {
                // Logs are propagated but not the session
                runInputs(inputs.tail, runState.recordLogStack(filledState.logStack))
              }
            )
        }
      }

    runState.scenarioContext.fillPlaceholders(where)
      .flatMap(CornichonJson.parseDataTable)
      .fold(
        t => Task.now(handleErrors(this, runState, NonEmptyList.one(t))),
        parsedTable => {
          val inputs = parsedTable.map { line =>
            line.toList.map { case (key, json) => (key, CornichonJson.jsonStringValue(json)) }
          }

          runInputs(inputs, runState.nestedContext)
            .timed
            .map {
              case (executionTime, (inputsState, inputsRes)) =>
                val initialDepth = runState.depth
                val (logStack, res) = inputsRes match {
                  case Right(_) =>
                    val wrappedLogStack = SuccessLogInstruction("With data input succeeded for all inputs", initialDepth, Some(executionTime)) +: inputsState.logStack :+ successTitleLog(initialDepth)
                    (wrappedLogStack, rightDone)
                  case Left((failedInputs, failedStep)) =>
                    val wrappedLogStack = FailureLogInstruction("With data input failed for one input", initialDepth, Some(executionTime)) +: inputsState.logStack :+ failedTitleLog(initialDepth)
                    val artificialFailedStep = FailedStep.fromSingle(failedStep.step, WithDataInputBlockFailedStep(failedInputs, failedStep.errors))
                    (wrappedLogStack, Left(artificialFailedStep))
                }
                (runState.mergeNested(inputsState, logStack), res)
            }
        }
      )
  }
}

case class WithDataInputBlockFailedStep(failedInputs: List[(String, String)], errors: NonEmptyList[CornichonError]) extends CornichonError {
  lazy val baseErrorMessage = s"WithDataInput block failed for inputs ${printArrowPairs(failedInputs)}"
  override val causedBy = errors.toList
} 
Example 139
Source File: RepeatStep.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.steps.wrapped

import cats.data.{ NonEmptyList, StateT }
import com.github.agourlay.cornichon.core._
import com.github.agourlay.cornichon.core.Done._
import monix.eval.Task

case class RepeatStep(nested: List[Step], occurrence: Int, indexName: Option[String]) extends WrapperStep {

  require(occurrence > 0, "repeat block must contain a positive number of occurrence")

  val title = s"Repeat block with occurrence '$occurrence'"

  override val stateUpdate: StepState = StateT { runState =>

    def repeatSuccessSteps(retriesNumber: Int, runState: RunState): Task[(Int, RunState, Either[FailedStep, Done])] = {
      // reset logs at each loop to have the possibility to not aggregate in failure case
      val rs = runState.resetLogStack
      val runStateWithIndex = indexName.fold(rs)(in => rs.addToSession(in, (retriesNumber + 1).toString))
      ScenarioRunner.runStepsShortCircuiting(nested, runStateWithIndex).flatMap {
        case (onceMoreRunState, stepResult) =>
          stepResult.fold(
            failed => {
              // In case of failure only the logs of the last run are shown to avoid giant traces.
              Task.now((retriesNumber, onceMoreRunState, Left(failed)))
            },
            _ => {
              val successState = runState.withSession(onceMoreRunState.session).recordLogStack(onceMoreRunState.logStack)
              // only show last successful run to avoid giant traces.
              if (retriesNumber == occurrence - 1) Task.now((retriesNumber, successState, rightDone))
              else repeatSuccessSteps(retriesNumber + 1, runState.withSession(onceMoreRunState.session))
            }
          )
      }
    }

    repeatSuccessSteps(0, runState.nestedContext)
      .timed
      .map {
        case (executionTime, run) =>
          val (retries, repeatedState, report) = run
          val depth = runState.depth
          val (logStack, res) = report.fold(
            failedStep => {
              val wrappedLogStack = FailureLogInstruction(s"Repeat block with occurrence '$occurrence' failed after '$retries' occurrence", depth, Some(executionTime)) +: repeatedState.logStack :+ failedTitleLog(depth)
              val artificialFailedStep = FailedStep.fromSingle(failedStep.step, RepeatBlockContainFailedSteps(retries, failedStep.errors))
              (wrappedLogStack, Left(artificialFailedStep))
            },
            _ => {
              val wrappedLockStack = SuccessLogInstruction(s"Repeat block with occurrence '$occurrence' succeeded", depth, Some(executionTime)) +: repeatedState.logStack :+ successTitleLog(depth)
              (wrappedLockStack, rightDone)
            }
          )
          (runState.mergeNested(repeatedState, logStack), res)
      }
  }
}

case class RepeatBlockContainFailedSteps(failedOccurrence: Int, errors: NonEmptyList[CornichonError]) extends CornichonError {
  lazy val baseErrorMessage = s"Repeat block failed at occurrence $failedOccurrence"
  override val causedBy = errors.toList
} 
Example 140
Source File: RepeatDuringStep.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.steps.wrapped

import java.util.concurrent.TimeUnit

import cats.data.{ NonEmptyList, StateT }
import com.github.agourlay.cornichon.core._
import com.github.agourlay.cornichon.core.Done._
import monix.eval.Task

import scala.concurrent.duration.FiniteDuration

case class RepeatDuringStep(nested: List[Step], duration: FiniteDuration) extends WrapperStep {

  val title = s"Repeat block during '$duration'"

  override val stateUpdate: StepState = StateT { runState =>
    val initialDepth = runState.depth

    def repeatStepsDuring(runState: RunState, duration: FiniteDuration, retriesNumber: Long): Task[(Long, RunState, Either[FailedStep, Done])] =
      ScenarioRunner.runStepsShortCircuiting(nested, runState.resetLogStack) // reset logs at each loop to have the possibility to not aggregate in failure case
        .timed
        .flatMap {
          case (executionTime, run) =>
            val (repeatedOnceMore, res) = run
            val remainingTime = duration - executionTime
            res.fold(
              failedStep => {
                // In case of failure only the logs of the last run are shown to avoid giant traces.
                Task.now((retriesNumber, repeatedOnceMore, Left(failedStep)))
              },
              _ => {
                val successState = runState.mergeNested(repeatedOnceMore)
                if (remainingTime.gt(FiniteDuration(0, TimeUnit.MILLISECONDS)))
                  repeatStepsDuring(successState, remainingTime, retriesNumber + 1)
                else
                  // In case of success all logs are returned but they are not printed by default.
                  Task.now((retriesNumber, successState, rightDone))
              }
            )
        }

    repeatStepsDuring(runState.nestedContext, duration, 0)
      .timed
      .map {
        case (executionTime, run) =>
          val (retries, repeatedRunState, report) = run
          val (logStack, res) = report.fold(
            failedStep => {
              val wrappedLogStack = FailureLogInstruction(s"Repeat block during '$duration' failed after being retried '$retries' times", initialDepth, Some(executionTime)) +: repeatedRunState.logStack :+ failedTitleLog(initialDepth)
              val artificialFailedStep = FailedStep.fromSingle(failedStep.step, RepeatDuringBlockContainFailedSteps(duration, failedStep.errors))
              (wrappedLogStack, Left(artificialFailedStep))
            },
            _ => {
              val wrappedLogStack = SuccessLogInstruction(s"Repeat block during '$duration' succeeded after '$retries' retries", initialDepth, Some(executionTime)) +: repeatedRunState.logStack :+ successTitleLog(initialDepth)
              (wrappedLogStack, rightDone)
            }
          )
          (runState.mergeNested(repeatedRunState, logStack), res)
      }
  }
}

case class RepeatDuringBlockContainFailedSteps(duration: FiniteDuration, errors: NonEmptyList[CornichonError]) extends CornichonError {
  lazy val baseErrorMessage = s"RepeatDuring block failed before '$duration'"
  override val causedBy = errors.toList
} 
Example 141
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 142
Source File: FeatureRunner.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.core

import com.github.agourlay.cornichon.dsl.BaseFeature
import com.github.agourlay.cornichon.matchers.MatcherResolver
import monix.eval.Task
import monix.reactive.Observable

case class FeatureRunner(featureDef: FeatureDef, baseFeature: BaseFeature, explicitSeed: Option[Long]) {

  private val featureContext = FeatureContext(
    beforeSteps = baseFeature.beforeEachScenario.toList,
    finallySteps = baseFeature.afterEachScenario.toList,
    featureIgnored = featureDef.ignored.isDefined,
    focusedScenarios = featureDef.focusedScenarios,
    withSeed = explicitSeed.orElse(baseFeature.seed),
    customExtractors = baseFeature.registerExtractors,
    allMatchers = (MatcherResolver.builtInMatchers ::: baseFeature.registerMatchers).groupBy(_.key)
  )

  final def runScenario(s: Scenario): Task[ScenarioReport] = {
    println(s"Starting scenario '${s.name}'")
    ScenarioRunner.runScenario(Session.newEmpty, featureContext)(s)
  }

  final def runFeature(filterScenario: Scenario => Boolean)(scenarioResultHandler: ScenarioReport => ScenarioReport): Task[List[ScenarioReport]] = {
    val scenariosToRun = featureDef.scenarios.filter(filterScenario)
    if (scenariosToRun.isEmpty)
      FeatureRunner.noop
    else {
      // Run 'before feature' hooks
      baseFeature.beforeFeature.foreach(f => f())
      // featureParallelism is limited to avoid spawning too much work at once
      val featureParallelism = if (baseFeature.executeScenariosInParallel) Math.min(scenariosToRun.size, FeatureRunner.maxParallelism) else 1
      Observable.fromIterable(scenariosToRun)
        .mapParallelUnordered(featureParallelism)(runScenario(_).map(scenarioResultHandler))
        .toListL
        .map { results =>
          // Run 'after feature' hooks
          baseFeature.afterFeature.foreach(f => f())
          results
        }
    }
  }

}

object FeatureRunner {
  // the tests are mostly IO bound so we can start a bit more on a single core
  lazy val maxParallelism: Int = Runtime.getRuntime.availableProcessors() + 1
  private val noop = Task.now(Nil)
} 
Example 143
Source File: ScenarioReport.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.core

import cats.data.Validated.Valid
import cats.data.{ NonEmptyList, ValidatedNel }
import cats.kernel.Monoid
import monix.eval.Task

import scala.concurrent.Future
import scala.concurrent.duration.{ Duration, FiniteDuration }

sealed trait ScenarioReport {
  def isSuccess: Boolean
  def scenarioName: String
  def session: Session
  def logs: List[LogInstruction]
  def duration: FiniteDuration
}

object ScenarioReport {
  def build(scenarioName: String, runState: RunState, result: ValidatedNel[FailedStep, Done], duration: FiniteDuration): ScenarioReport =
    result.fold(
      failedSteps => FailureScenarioReport(scenarioName, failedSteps, runState.session, runState.logStack, duration, runState.randomContext.initialSeed),
      _ => SuccessScenarioReport(scenarioName, runState.session, runState.logStack, duration, runState.randomContext.initialSeed)
    )
}

case class SuccessScenarioReport(scenarioName: String, session: Session, logStack: List[LogInstruction], duration: FiniteDuration, seed: Long) extends ScenarioReport {
  val isSuccess = true

  // keeping it lazy to avoid the reverse in case of no rendering
  lazy val logs = logStack.reverse
  // In case of success, logs are only shown if the scenario contains DebugLogInstruction
  lazy val shouldShowLogs: Boolean = logStack.exists(_.isInstanceOf[DebugLogInstruction])
}

case class IgnoreScenarioReport(scenarioName: String, reason: String, session: Session) extends ScenarioReport {
  val logs = Nil
  val isSuccess = false
  val duration = Duration.Zero
}

case class PendingScenarioReport(scenarioName: String, session: Session) extends ScenarioReport {
  val logs = Nil
  val isSuccess = false
  val duration = Duration.Zero
}

case class FailureScenarioReport(scenarioName: String, failedSteps: NonEmptyList[FailedStep], session: Session, logStack: List[LogInstruction], duration: FiniteDuration, seed: Long) extends ScenarioReport {
  val isSuccess = false

  val msg =
    s"""|Scenario '$scenarioName' failed:
        |${failedSteps.toList.iterator.map(_.messageForFailedStep).mkString("\nand\n")}
        |seed for the run was '$seed'
        |""".stripMargin

  lazy val logs = logStack.reverse
  lazy val renderedColoredLogs = LogInstruction.renderLogs(logs)
  lazy val renderedLogs = LogInstruction.renderLogs(logs, colorized = false)
}

sealed abstract class Done
case object Done extends Done {
  val rightDone = Right(Done)
  val validDone = Valid(Done)
  val futureDone = Future.successful(Done)
  val taskDone = Task.now(Done)
  implicit val monoid = new Monoid[Done] {
    def empty: Done = Done
    def combine(x: Done, y: Done): Done = x
  }
}

case class FailedStep(step: Step, errors: NonEmptyList[CornichonError]) {
  lazy val messageForFailedStep =
    s"""
       |at step:
       |${step.title}
       |
       |with error(s):
       |${errors.toList.iterator.map(_.renderedMessage).mkString("\nand\n")}
       |""".stripMargin

}

object FailedStep {
  def fromSingle(step: Step, error: CornichonError) = FailedStep(step, NonEmptyList.one(error))
} 
Example 144
Source File: MatchersProperties.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.matchers

import java.time.Instant
import java.time.format.DateTimeFormatter

import com.github.agourlay.cornichon.matchers.Matchers._
import io.circe.Json
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.scalacheck._
import org.scalacheck.Prop._
import org.typelevel.claimant.Claim

object MatchersProperties extends Properties("Matchers") {

  val reasonablyRandomInstantGen: Gen[Instant] = for {
    randomOffset <- Arbitrary.arbLong.arbitrary
  } yield Instant.now().plusMillis(randomOffset % 1000000000000L)

  val instantGen: Gen[Instant] = for {
    randomOffset <- Arbitrary.arbLong.arbitrary
  } yield Instant.now().plusMillis(randomOffset)

  property("any-integer correct for any int") =
    forAll(Gen.size) { int =>
      Claim {
        anyInteger.predicate(Json.fromInt(int))
      }
    }

  property("any-integer incorrect for any alphanum string") =
    forAll(Gen.alphaNumStr) { alphanum =>
      Claim {
        !anyInteger.predicate(Json.fromString(alphanum))
      }
    }

  property("any-positive-integer correct for any positive int") =
    forAll(Gen.choose(1, Int.MaxValue)) { int =>
      Claim {
        anyPositiveInteger.predicate(Json.fromInt(int))
      }
    }

  property("any-positive-integer incorrect for any alphanum string") =
    forAll(Gen.alphaNumStr) { alphanum =>
      Claim {
        !anyPositiveInteger.predicate(Json.fromString(alphanum))
      }
    }

  property("any-negative-integer correct for any negative int") =
    forAll(Gen.negNum[Int]) { int =>
      Claim {
        anyNegativeInteger.predicate(Json.fromInt(int))
      }
    }

  property("any-negative-integer incorrect for any alphanum string") =
    forAll(Gen.alphaNumStr) { alphanum =>
      Claim {
        !anyNegativeInteger.predicate(Json.fromString(alphanum))
      }
    }

  property("any-uuid correct for any valid UUID") =
    forAll(Gen.uuid) { uuid =>
      Claim {
        anyUUID.predicate(Json.fromString(uuid.toString))
      }
    }

  property("any-uuid incorrect for any alphanum string") =
    forAll(Gen.alphaNumStr) { alphanum =>
      Claim {
        !anyUUID.predicate(Json.fromString(alphanum))
      }
    }

  property("any-date-time correct for all ISO-compliant values, including Y10K+ dates") =
    forAll(instantGen) { instant =>
      Claim {
        anyDateTime.predicate(Json.fromString(DateTimeFormatter.ISO_INSTANT.format(instant)))
      }
    }

  property("any-date-time correct in parallel") = {
    forAll(reasonablyRandomInstantGen) { instant =>
      val booleans = 1.to(64).map { _ =>
        Task.delay {
          anyDateTime.predicate(Json.fromString(DateTimeFormatter.ISO_INSTANT.format(instant)))
        }
      }

      val res = Task.parSequenceUnordered(booleans).runSyncUnsafe().foldLeft(List.empty[Boolean]) { case (acc, e) => e :: acc }

      Claim(res.forall(_ == true))
    }
  }
} 
Example 145
Source File: EffectStepSpec.scala    From cornichon   with Apache License 2.0 5 votes vote down vote up
package com.github.agourlay.cornichon.steps.regular

import com.github.agourlay.cornichon.core.{ CornichonError, Scenario, ScenarioRunner, Session }
import com.github.agourlay.cornichon.steps.cats.{ EffectStep => CEffectStep }
import com.github.agourlay.cornichon.testHelpers.CommonTestSuite
import monix.eval.Task
import utest._

import scala.concurrent.Future
import scala.util.control.NoStackTrace

object EffectStepSpec extends TestSuite with CommonTestSuite {

  val tests = Tests {
    test("EffectStep Async - return error if an Effect step throw an exception") {
      val step = EffectStep(title = "buggy effect", _ => throwExceptionWithStackTrace())
      val s = Scenario("scenario with broken effect step", step :: Nil)
      val res = awaitTask(ScenarioRunner.runScenario(Session.newEmpty)(s))
      scenarioFailsWithMessage(res) {
        """|Scenario 'scenario with broken effect step' failed:
           |
           |at step:
           |buggy effect
           |
           |with error(s):
           |exception thrown com.github.agourlay.cornichon.testHelpers.CommonTesting$$anon$1: boom!
           |
           |
           |seed for the run was '1'
           |""".stripMargin
      }
    }

    test("EffectStep Async - return error if an Effect step for Future.failed") {
      val step = EffectStep(title = "buggy effect", _ => Future.failed(new RuntimeException("boom")))
      val s = Scenario("scenario with broken effect step", step :: Nil)
      val res = awaitTask(ScenarioRunner.runScenario(Session.newEmpty)(s))
      assert(!res.isSuccess)
    }

    test("EffectStep Sync - return error if an Effect step throw an exception") {
      val step = EffectStep.fromSync(title = "buggy effect", _ => throwExceptionWithStackTrace())
      val s = Scenario("scenario with broken effect step", step :: Nil)
      val res = awaitTask(ScenarioRunner.runScenario(Session.newEmpty)(s))
      assert(!res.isSuccess)
    }

    test("EffectStep SyncE - valid if effect is an Either.Right") {
      val step = EffectStep.fromSyncE(title = "valid effect", sc => Right(sc.session))
      val s = Scenario("scenario with valid effect step", step :: Nil)
      val res = awaitTask(ScenarioRunner.runScenario(Session.newEmpty)(s))
      assert(res.isSuccess)
    }

    test("EffectStep SyncE - invalid if effect is an Either.Left") {
      val step = EffectStep.fromSyncE(title = "valid effect", _ => Left(CornichonError.fromString("ohh nooes")))
      val s = Scenario("scenario with invalid effect step", step :: Nil)
      val res = awaitTask(ScenarioRunner.runScenario(Session.newEmpty)(s))
      assert(!res.isSuccess)
    }

    test("CatsEffectStep Async - return error if an Effect step throw an exception") {
      val step = CEffectStep[Task](title = "buggy effect", _ => throwExceptionWithStackTrace())
      val s = Scenario("scenario with broken effect step", step :: Nil)
      val res = awaitTask(ScenarioRunner.runScenario(Session.newEmpty)(s))
      assert(!res.isSuccess)
    }

    test("CatsEffectStep Async - return error if an Effect step for Task.raiseError") {
      val step = CEffectStep[Task](title = "buggy effect", _ => Task.raiseError(new RuntimeException("boom") with NoStackTrace))
      val s = Scenario("scenario with broken effect step", step :: Nil)
      val res = awaitTask(ScenarioRunner.runScenario(Session.newEmpty)(s))
      assert(!res.isSuccess)
    }

    test("CatsEffectStep Sync - return error if an Effect step throw an exception") {
      val step = CEffectStep[Task](title = "buggy effect", _ => throwExceptionWithStackTrace())
      val s = Scenario("scenario with broken effect step", step :: Nil)
      val res = awaitTask(ScenarioRunner.runScenario(Session.newEmpty)(s))
      assert(!res.isSuccess)
    }

    test("CatsEffectStep SyncE - valid if effect is an Either.Right") {
      val step = CEffectStep.fromSyncE(title = "valid effect", sc => Right(sc.session))
      val s = Scenario("scenario with valid effect step", step :: Nil)
      val res = awaitTask(ScenarioRunner.runScenario(Session.newEmpty)(s))
      assert(res.isSuccess)
    }

    test("CatsEffectStep SyncE - invalid if effect is an Either.Left") {
      val step = CEffectStep.fromSyncE(title = "valid effect", _ => Left(CornichonError.fromString("ohh nooes")))
      val s = Scenario("scenario with invalid effect step", step :: Nil)
      val res = awaitTask(ScenarioRunner.runScenario(Session.newEmpty)(s))
      assert(!res.isSuccess)
    }
  }
} 
Example 146
Source File: DoobieInstancesSuite.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.doobie

import cats.Applicative
import cats.data.ReaderT
import cats.effect.{IO, SyncIO}
import com.github.ghik.silencer.silent
import doobie.ConnectionIO
import monix.eval.{Coeval, Task}
import monix.execution.Scheduler
import tofu.doobie.instances.implicits._
import tofu.env.Env
import tofu.lift.Lift
import tofu.zioInstances.implicits._
import zio.interop.catz._

object DoobieInstancesSuite {

  def summonImplicitsViaLiftToIO[F[_]: Applicative, R](implicit L: Lift[F, IO]): Unit = {
    Lift[F, ConnectionIO]
    Lift[F, ConnectionRIO[R, *]]
    Lift[ReaderT[F, R, *], ConnectionRIO[R, *]]
    ()
  }

  def summonCatsEffectImplicits[R](): Unit = {
    Lift[SyncIO, ConnectionIO]
    Lift[SyncIO, ConnectionRIO[R, *]]
    Lift[ReaderT[SyncIO, R, *], ConnectionRIO[R, *]]

    Lift[IO, ConnectionIO]
    Lift[IO, ConnectionRIO[R, *]]
    Lift[ReaderT[IO, R, *], ConnectionRIO[R, *]]

    ()
  }

  def summonMonixImplicitsViaScheduler[R](implicit sc: Scheduler): Unit = {
    Lift[Coeval, ConnectionIO]
    Lift[Coeval, ConnectionRIO[R, *]]
    Lift[ReaderT[Coeval, R, *], ConnectionRIO[R, *]]

    Lift[Task, ConnectionIO]
    Lift[Task, ConnectionRIO[R, *]]
    Lift[ReaderT[Task, R, *], ConnectionRIO[R, *]]

    Lift[Env[R, *], ConnectionRIO[R, *]]

    ()
  }

  def summonMonixImplicitsUnambiguously[R](implicit @silent sc: Scheduler, L: Lift[Task, IO]): Unit = {
    Lift[Task, ConnectionIO]
    Lift[Task, ConnectionRIO[R, *]]
    Lift[ReaderT[Task, R, *], ConnectionRIO[R, *]]
    ()
  }

  def summonZioImplicits[R](): zio.Task[Unit] =
    zio.Task.concurrentEffect.map { implicit CE =>
      Lift[zio.Task, ConnectionIO]
      Lift[zio.Task, ConnectionRIO[R, *]]
      Lift[zio.RIO[R, *], ConnectionRIO[R, *]]
      ()
    }

  def summonLiftConnectionIO[R](): Unit = {
    LiftConnectionIO[ConnectionIO]
    LiftConnectionIO[ConnectionRIO[R, *]]
    ()
  }

} 
Example 147
Source File: EnvSyntax.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.env

import monix.eval.Task

private[env] trait EnvSyntax {
  implicit def toEnvOptionOps[E, A](env: Env[E, Option[A]]): EnvOptionOps[E, A] = new EnvOptionOps(env)
}

class EnvOptionOps[E, A](val ea: Env[E, Option[A]]) extends AnyVal {
  import EnvOptionOps._
  def orElseF[B >: A](eb: Env[E, Option[B]]): Env[E, Option[B]] =
    (ea, eb) match {
      case (EnvTask(ta), EnvTask(tb)) =>
        EnvTask(ta.flatMap {
          case None        => tb
          case s @ Some(_) => Task.pure(s)
        })
      case _                          =>
        ea.flatMap {
          case None        => eb
          case s @ Some(_) => Env.pure(s)
        }
    }

  def orElseT[B >: A](tb: Task[Option[B]]): Env[E, Option[B]] =
    ea.mapTask(_.flatMap {
      case None        => tb
      case s @ Some(_) => Task.pure(s)
    })

  def orElse[B >: A](b: => Option[B]): Env[E, Option[B]] =
    ea.mapTask(_.map(_.orElse(b)))

  def getOrElseF[B >: A](eb: Env[E, B]): Env[E, B] =
    eb match {
      case EnvTask(tb) => ea.mapTask(getOrElseTask(tb))
      case _           => Env(ctx => getOrElseTask(eb.run(ctx))(ea.run(ctx)))
    }
  def getOrElseT[B >: A](tb: Task[B]): Env[E, B]   = ea.mapTask(getOrElseTask(tb))
  def getOrElse[B >: A](a: => B): Env[E, B]        = ea.map(_.getOrElse(a))

  def orRaise(e: Throwable): Env[E, A] =
    ea.mapTask(_.flatMap(_.fold(Task.raiseError[A](e))(Task.pure)))
}

object EnvOptionOps {
  @inline private def getOrElseTask[A](tb: Task[A])(ta: Task[Option[A]]): Task[A] =
    ta.flatMap(_.fold(tb)(Task.pure))
} 
Example 148
Source File: Memoization.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.env

import monix.catnap.MVar
import monix.eval.Task

final class Memoization[A] private (memo: MVar[Task, Option[A]]) {
  private def initWith(x: Task[A]): Task[A] =
    memo.take.bracketE(_.fold(x)(Task.pure)) {
      case (_, Right(b)) => memo.put(Some(b))
      case (prev, _)     => memo.put(prev)
    }

  def getOrElse(x: Task[A]): Task[A] =
    for {
      state <- memo.read
      value <- state.fold(initWith(x))(Task.pure)
    } yield value
}

object Memoization {
  def apply[A]: Task[Task[Memoization[A]]] =
    Task.delay(unsafe())

  def unsafe[A](): Task[Memoization[A]] =
    MVar[Task].of(Option.empty[A]).map(mvar => new Memoization[A](mvar)).memoize
} 
Example 149
Source File: EnvInstances.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.env

import cats.arrow.{ArrowChoice, FunctionK, Profunctor}
import cats.effect.IO
import cats.{Applicative, Monad, Parallel, ~>}
import monix.eval.{Task, TaskLift}
import monix.execution.Scheduler
import tofu.lift.{UnliftIO, UnsafeExecFuture}
import tofu.optics.Contains
import tofu.syntax.funk._

import scala.concurrent.Future

private[env] trait EnvInstances {
  self: Env.type =>

  private object anyEnvInstance extends EnvFunctorstance[Any]

  final implicit def envInstance[E]: EnvFunctorstance[E] = anyEnvInstance.asInstanceOf[EnvFunctorstance[E]]

  private object envParallelInstance extends Applicative[Env[Any, *]] {
    override def pure[A](x: A): Env[Any, A]                                                   = Env.pure(x)
    override def ap[A, B](ff: Env[Any, A => B])(fa: Env[Any, A]): Env[Any, B]                 =
      Env.parMap2(ff, fa)(_.apply(_))
    override def map2[A, B, Z](fa: Env[Any, A], fb: Env[Any, B])(f: (A, B) => Z): Env[Any, Z] =
      Env.parMap2(fa, fb)(f)
    override val unit: Env[Any, Unit]                                                         = Env.unit
    override def map[A, B](fa: Env[Any, A])(f: A => B): Env[Any, B]                           = fa.map(f)
    override def replicateA[A](n: Int, fa: Env[Any, A]): Env[Any, List[A]]                    =
      fa.mapTask(t => Task.parSequence(Iterable.fill(n)(t)).map(_.toList))
  }

  private object anyEnvParallelInstance extends Parallel[Env[Any, *]] {
    type F[a] = Env[Any, a]
    override def applicative: Applicative[Env[Any, *]]    = envParallelInstance
    override def monad: Monad[Env[Any, *]]                = anyEnvInstance
    override val sequential: ~>[Env[Any, *], Env[Any, *]] = FunctionK.id
    override val parallel: ~>[Env[Any, *], Env[Any, *]]   = FunctionK.id
  }

  final implicit def envParallelInstance[E]: Parallel[Env[E, *]] =
    anyEnvParallelInstance.asInstanceOf[Parallel[Env[E, *]]]

  final implicit val envProfuctorInstance: Profunctor[Env] with ArrowChoice[Env] =
    new Profunctor[Env] with ArrowChoice[Env] {
      override def choose[A, B, C, D](f: Env[A, C])(g: Env[B, D]): Env[Either[A, B], Either[C, D]] =
        Env {
          case Left(a)  => f.run(a).map(Left(_))
          case Right(b) => g.run(b).map(Right(_))
        }

      override def lift[A, B](f: A => B): Env[A, B]                                   = Env(a => Task.pure(f(a)))
      override def first[A, B, C](fa: Env[A, B]): Env[(A, C), (B, C)]                 =
        fa.first[C]
      override def second[A, B, C](fa: Env[A, B]): Env[(C, A), (C, B)]                =
        fa.second[C]
      override def compose[A, B, C](f: Env[B, C], g: Env[A, B]): Env[A, C]            =
        f.compose(g)
      override def rmap[A, B, C](fab: Env[A, B])(f: B => C): Env[A, C]                =
        fab.map(f)
      override def lmap[A, B, C](fab: Env[A, B])(f: C => A): Env[C, B]                =
        fab.localP(f)
      override def id[A]: Env[A, A]                                                   = Env.context
      override def dimap[A, B, C, D](fab: Env[A, B])(f: C => A)(g: B => D): Env[C, D] = fab.dimap(f)(g)
      override def split[A, B, C, D](f: Env[A, B], g: Env[C, D]): Env[(A, C), (B, D)] =
        f.split(g)
      override def left[A, B, C](fab: Env[A, B]): Env[Either[A, C], Either[B, C]]     = fab.left[C]
      override def right[A, B, C](fab: Env[A, B]): Env[Either[C, A], Either[C, B]]    = fab.right[C]
      override def choice[A, B, C](f: Env[A, C], g: Env[B, C]): Env[Either[A, B], C]  =
        f.choice(g)
      override def merge[A, B, C](f: Env[A, B], g: Env[A, C]): Env[A, (B, C)]         =
        Env.parZip2(f, g)
    }

  final implicit def envUnliftSubContext[E, E1: E Contains *]: EnvUnliftSubContext[E, E1] = new EnvUnliftSubContext

  def envUnsafeExecFuture[E](implicit sc: Scheduler): UnsafeExecFuture[Env[E, *]] =
    new UnsafeExecFuture[Env[E, *]] {
      def lift[A](fa: Future[A]): Env[E, A]   = Env.fromFuture(fa)
      def unlift: Env[E, Env[E, *] ~> Future] = Env.fromFunc(r => makeFunctionK(_.run(r).runToFuture))
    }

  implicit def envUnliftIO[E](implicit toIO: TaskLift[IO]): UnliftIO[Env[E, *]] = new UnliftIO[Env[E, *]] {
    def lift[A](fa: IO[A]): Env[E, A]   = Env.fromIO(fa)
    def unlift: Env[E, Env[E, *] ~> IO] = Env.fromFunc(r => funK(_.run(r).to[IO]))
  }
} 
Example 150
Source File: EnvRacing.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.env

import cats.effect._
import monix.eval.Task
import scala.collection.compat._

private[env] trait EnvRacing {
  self: Env.type =>
  type Racing[F[_], A, B] = Either[(A, Fiber[F, B]), (Fiber[F, A], B)]

  private def convertRacingFibers[E, A, B]: Racing[Task, A, B] => Racing[Env[E, *], A, B] = {
    case Left((a, fibB))  => Left((a, EnvFiber(fibB)))
    case Right((fibA, b)) => Right((EnvFiber(fibA), b))
  }

  def racePair[E, A, B](
      ta: Env[E, A],
      tb: Env[E, B]
  ): Env[E, Either[(A, Fiber[Env[E, *], B]), (Fiber[Env[E, *], A], B)]] =
    (ta, tb) match {
      case (EnvTask(taskA), EnvTask(taskB)) =>
        fromTask(Task.racePair(taskA, taskB).map(convertRacingFibers))
      case _                                =>
        Env(ctx => Task.racePair(ta.run(ctx), tb.run(ctx)).map(convertRacingFibers))
    }

  def race[E, A, B](ta: Env[E, A], tb: Env[E, B]): Env[E, Either[A, B]] =
    (ta, tb) match {
      case (EnvTask(taskA), EnvTask(taskB)) => fromTask(Task.race(taskA, taskB))
      case _                                => Env(ctx => Task.race(ta.run(ctx), tb.run(ctx)))
    }

  def raceMany[E, A](tta: IterableOnce[Env[E, A]]): Env[E, A] = {
    val taskAccum = Array.newBuilder[Task[A]]
    val funcAccum = Array.newBuilder[E => Task[A]]
    tta.iterator.foreach {
      case EnvTask(tt) => taskAccum += tt
      case env         =>
        funcAccum += env.run
    }
    val tasks     = taskAccum.result()
    val funcs     = funcAccum.result()
    if (funcs.isEmpty) EnvTask(Task.raceMany(tasks))
    else Env(ctx => Task.raceMany(funcs.map(_(ctx)) ++ tasks))
  }
}

final private[env] case class EnvFiber[E, A](taskFiber: Fiber[Task, A]) extends Fiber[Env[E, *], A] {
  override def cancel: Env[E, Unit] = Env.fromTask(taskFiber.cancel)
  override def join: Env[E, A]      = Env.fromTask(taskFiber.join)
} 
Example 151
Source File: EnvFunctions.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.env

import java.util.concurrent.TimeUnit.MILLISECONDS

import cats.Eval
import cats.data.ReaderT
import cats.effect._
import monix.eval.{Coeval, Task}
import monix.execution.Scheduler

import scala.concurrent.duration.FiniteDuration
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try

private[env] trait EnvFunctions
    extends EnvProducts with EnvTraversing with EnvRacing with EnvSyntax with EnvTransformations {
  self: Env.type =>
  def apply[E, A](f: E => Task[A]): Env[E, A] = EnvCtx(f)
  def later[E, A](x: => A): Env[E, A]         = fromTask(Task.delay(x))
  def pure[E, A](x: A): Env[E, A]             = fromTask(Task.pure(x))

  def context[E]: Env[E, E]                            = Env(ctx => Task.now(ctx))
  def withContextNow[E, A](f: E => A): Env[E, A]       = Env(ctx => Task.now(f(ctx)))
  def withContext[E, A](f: E => Env[E, A]): Env[E, A]  = Env(ctx => f(ctx).run(ctx))
  def withContextFork[E, A](f: E => A): Env[E, A]      = Env(ctx => Task(f(ctx)))
  def withContextDelay[E, A](f: E => A): Env[E, A]     = Env(ctx => Task.delay(f(ctx)))
  private[this] val anyUnit: Env[Any, Unit]            = fromTask(Task.unit)
  def unit[E]: Env[E, Unit]                            = anyUnit.asInstanceOf[Env[E, Unit]]
  def scheduler[E]: Env[E, Scheduler]                  =
    fromTask(Task.deferAction(scheduler => Task.pure(scheduler)))
  def millis[E]: Env[E, Long]                          =
    fromTask(Task.deferAction(scheduler => Task.pure(scheduler.clockRealTime(MILLISECONDS))))
  def shift[E]: Env[E, Unit]                           = fromTask(Task.shift)
  def shift[E](ec: ExecutionContext): Env[E, Unit]     = fromTask(Task.shift(ec))
  def sleep[E](duration: FiniteDuration): Env[E, Unit] =
    fromTask(Task.sleep(duration))

  def fromTask[E, A](task: Task[A]): Env[E, A]                           = EnvTask(task)
  def delay[E, A](x: => A): Env[E, A]                                    = later(x)
  def defer[E, A](x: => Env[E, A]): Env[E, A]                            =
    Env(ctx => Task.defer(x.run(ctx)))
  def deferTask[E, A](x: => Task[A]): Env[E, A]                          = fromTask(Task.defer(x))
  def deferFuture[E, A](future: => Future[A]): Env[E, A]                 =
    EnvTask(Task.deferFuture(future))
  def deferFutureContext[E, A](future: E => Future[A]): Env[E, A]        =
    Env(ctx => Task.deferFuture(future(ctx)))
  def deferFutureAction[E, A](futAct: Scheduler => Future[A]): Env[E, A] =
    EnvTask(Task.deferFutureAction(futAct))
  def raiseError[E, A](throwable: Throwable): Env[E, A]                  =
    EnvTask(Task.raiseError(throwable))
  def attempt[E, A](e: Env[E, A]): Env[E, Either[Throwable, A]]          = e.attempt

  def fromFunc[E, A](f: E => A): Env[E, A]                          = Env.withContextNow(f)
  def fromReaderT[E, A](reader: ReaderT[Task, E, A]): Env[E, A]     =
    Env(reader.run)
  def fromTry[E, A](t: Try[A]): Env[E, A]                           = fromTask(Task.fromTry(t))
  def fromEither[E, A](e: Either[Throwable, A]): Env[E, A]          = fromTask(Task.fromTry(e.toTry))
  def fromFuture[E, A](future: Future[A]): Env[E, A]                = EnvTask(Task.fromFuture(future))
  def fromTryFunc[E, A](ft: E => Try[A]): Env[E, A]                 =
    Env(ctx => Task.fromTry(ft(ctx)))
  def fromEffect[F[_]: Effect, E, A](fa: F[A]): Env[E, A]           =
    fromTask(Task.fromEffect(fa))
  def fromEffectFunc[F[_]: Effect, E, A](ffa: E => F[A]): Env[E, A] =
    Env(ctx => Task.fromEffect(ffa(ctx)))
  def fromEval[E, A](ea: Eval[A]): Env[E, A]                        = fromTask(Task.from(ea))
  def fromEvalFunc[E, A](fea: E => Eval[A]): Env[E, A]              =
    Env(ctx => Task.from(fea(ctx)))
  def fromIO[E, A](ioa: IO[A]): Env[E, A]                           = fromTask(Task.from(ioa))
  def fromIOFunc[E, A](fioa: E => IO[A]): Env[E, A]                 =
    Env(ctx => Task.from(fioa(ctx)))
  def fromCoeval[E, A](ca: Coeval[A]): Env[E, A]                    = fromTask(ca.to[Task])
  def fromCoevalFunc[E, A](fca: E => Coeval[A]): Env[E, A]          =
    Env(ctx => fca(ctx).to[Task])

  def tailRecM[E, A, B](a: A)(f: (A) => Env[E, Either[A, B]]): Env[E, B] =
    Env(ctx => Task.tailRecM(a)(a1 => f(a1).run(ctx)))

  def when[E](cond: => Boolean)(io: => Env[E, _]): Env[E, Unit] =
    whenF(delay[E, Boolean](cond))(io)

  def whenF[E](cond: Env[E, Boolean])(io: => Env[E, _]): Env[E, Unit] =
    cond.flatMap(run => if (run) io.flatMap(_ => Env.unit[E]) else Env.unit[E])
} 
Example 152
Source File: EnvTraversing.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.env

import monix.eval.Task
import monix.execution.compat.BuildFrom
import tofu.env.internal.CollectionMapper

private[env] trait EnvTraversing {
  def sequence[E, A, M[X] <: Iterable[X]](
      in: M[Env[E, A]]
  )(implicit cbf: BuildFrom[M[Env[E, A]], A, M[A]]): Env[E, M[A]] =
    Env(ctx => Task.traverse(in)(_.run(ctx)))

  def traverse[E, A, B, M[X] <: Iterable[X]](
      in: M[A]
  )(f: A => Env[E, B])(implicit cbf: BuildFrom[M[A], B, M[B]]): Env[E, M[B]] =
    Env(ctx => Task.traverse(in)(x => f(x).run(ctx)))

  def gather[E, A, M[X] <: Iterable[X]](
      in: M[Env[E, A]]
  )(implicit cbf: BuildFrom[M[Env[E, A]], A, M[A]]): Env[E, M[A]] =
    Env(ctx => Task.parTraverse(in)(_.run(ctx)))

  def wander[E, A, B, M[X] <: Iterable[X]](
      in: M[A]
  )(f: A => Env[E, B])(implicit cbf: BuildFrom[M[A], B, M[B]]): Env[E, M[B]] =
    Env(ctx => Task.parTraverse(in)(x => f(x).run(ctx)))

  def gatherUnordered[E, A](in: Iterable[Env[E, A]]): Env[E, List[A]] =
    Env(ctx => Task.parTraverseUnordered(in)(_.run(ctx)))

  def wanderUnordered[E, A, B, M[X] <: Iterable[X]](in: M[A])(f: A => Env[E, B]): Env[E, List[B]] =
    Env(ctx => Task.parTraverseUnordered(in)(x => f(x).run(ctx)))

  
  object opt {
    type EitherAllTasks[M[_], E, A] = Either[M[Env[E, A]], M[EnvTask[E, A]]]

    @inline private[this] def walkTasks[E, A, M[X] <: Iterable[X]](
        in: M[Env[E, A]],
        fallback: => Env[E, M[A]],
        task: M[Env[E, A]] => (Env[E, A] => Task[A]) => Task[M[A]]
    ): Env[E, M[A]] =
      if (in.forall(_.isInstanceOf[EnvTask[E, A]])) Env.fromTask(task(in)(_.asInstanceOf[EnvTask[E, A]].ta))
      else fallback

    def sequence[E, A, M[+X] <: Iterable[X]](
        in: M[Env[E, A]]
    )(implicit cb: BuildFrom[M[Env[E, A]], A, M[A]]): Env[E, M[A]] =
      walkTasks(in, Env.sequence(in), Task.traverse[Env[E, A], A, M])

    def traverse[E, A, B, M[+X] <: Iterable[X]](in: M[A])(
        f: A => Env[E, B]
    )(implicit mapper: CollectionMapper[A, Env[E, B], M], cbf2: BuildFrom[M[Env[E, B]], B, M[B]]): Env[E, M[B]] =
      sequence(mapper.map(in, f))

    def parSequence[E, A, M[X] <: Iterable[X]](
        in: M[Env[E, A]]
    )(implicit cbf: BuildFrom[M[Env[E, A]], A, M[A]]): Env[E, M[A]] =
      walkTasks(in, Env.gather(in), Task.parTraverse[Env[E, A], A, M])

    def parTraverse[E, A, B, M[X] <: Iterable[X]](in: M[A])(
        f: A => Env[E, B]
    )(implicit mapper: CollectionMapper[A, Env[E, B], M], cbf2: BuildFrom[M[Env[E, B]], B, M[B]]): Env[E, M[B]] =
      parSequence(mapper.map(in, f))

    def parSequenceUnordered[E, A](in: Iterable[Env[E, A]]): Env[E, List[A]] =
      walkTasks(in.toList, Env.gatherUnordered(in), Task.parTraverseUnordered[Env[E, A], A, List])

    def parTraverseUnordered[E, A, B, M[X] <: Iterable[X]](in: M[A])(f: A => Env[E, B]): Env[E, List[B]] =
      parSequenceUnordered(in.map(f))

    @deprecated("use parSequence", since = "0.7.6")
    def gather[E, A, M[X] <: Iterable[X]](
        in: M[Env[E, A]]
    )(implicit cbf: BuildFrom[M[Env[E, A]], A, M[A]]): Env[E, M[A]] =
      parSequence(in)

    @deprecated("use parTraverse", since = "0.7.6")
    def wander[E, A, B, M[X] <: Iterable[X]](in: M[A])(
        f: A => Env[E, B]
    )(implicit mapper: CollectionMapper[A, Env[E, B], M], cbf2: BuildFrom[M[Env[E, B]], B, M[B]]): Env[E, M[B]] =
      parTraverse(in)(f)

    @deprecated("use parTraverseUnordered", since = "0.7.6")
    def wanderUnordered[E, A, B, M[X] <: Iterable[X]](in: M[A])(f: A => Env[E, B]): Env[E, List[B]] =
      parTraverseUnordered(in)(f)

    @deprecated("use parSequenceUnordered", since = "0.7.6")
    def gatherUnordered[E, A](in: Iterable[Env[E, A]]): Env[E, List[A]] =
      parSequenceUnordered(in)
  }
} 
Example 153
Source File: EnvBioSpecializedFunctions.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.env.bio

import monix.eval.Task

trait EnvBioSpecializedFunctions[R, E] {
  type F[A] = EnvBio[R, E, A]

  def apply[A](f: R => Task[Either[E, A]]): EnvBio[R, E, A] = EnvBio.apply(f)
  def applyFatal[A](f: R => Task[A]): EnvBio[R, E, A]       = EnvBio.applyFatal(a => f(a))
  def pure[A](x: A): EnvBio[R, E, A]                        = EnvBio.pure(x)
  def raiseError(e: E): EnvBio[R, E, Nothing]               = EnvBio.raiseError(e)

  def context: EnvBio[R, E, R]                               = EnvBio.context
  def fromTask[A](task: Task[A]): EnvBio[R, Throwable, A]    = EnvBio.fromTask(task)
  def fromTaskTotal[A](task: Task[A]): EnvBio[R, Nothing, A] = EnvBio.fromTaskTotal(task)

  def map2[A, B, C, R1 <: R, E1 >: E](ea: EnvBio[R, E, A], eb: EnvBio[R1, E1, B])(f: (A, B) => C): EnvBio[R1, E1, C] =
    EnvBio.map2(ea, eb)(f)

  def map3[A, B, C, D, R1 <: R, E1 >: E](ea: EnvBio[R, E, A], eb: EnvBio[R1, E1, B], ec: EnvBio[R1, E1, C])(
      f: (A, B, C) => D
  ): EnvBio[R1, E1, D] = EnvBio.map3(ea, eb, ec)(f)

  def parMap2[A, B, C, R1 <: R, E1 >: E](ea: EnvBio[R, E, A], eb: EnvBio[R1, E1, B])(
      f: (A, B) => C
  ): EnvBio[R1, E1, C] = EnvBio.parMap2(ea, eb)(f)

  def parMap3[A, B, C, D, R1 <: R, E1 >: E](ea: EnvBio[R, E, A], eb: EnvBio[R1, E1, B], ec: EnvBio[R1, E1, C])(
      f: (A, B, C) => D
  ): EnvBio[R1, E1, D] = EnvBio.parMap3(ea, eb, ec)(f)
} 
Example 154
Source File: EnvBio.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.env.bio

import monix.eval.Task
import scala.concurrent.duration.FiniteDuration

abstract class EnvBio[-R, +E, +A] {
  // todo should expose this to user?
  protected def runF(ctx: R): Task[A]

  final def run(ctx: R): Task[Either[E, A]] = runF(ctx).attempt.flatMap {
    case Left(UserError(err: E @unchecked)) => Task.pure(Left(err)) // todo erasure elimination?
    case Left(fatalErr)                     => Task.raiseError(fatalErr)
    case Right(value)                       => Task.pure(Right(value))
  }

  def mapTask[B](f: Task[A] => Task[B]): EnvBio[R, E, B] =
    EnvBio.applyFatal(ctx => f(runF(ctx)))

  def map[B](f: A => B): EnvBio[R, E, B] =
    mapTask(_.map(f))

  def flatMap[R1 <: R, E1 >: E, B](f: A => EnvBio[R1, E1, B]): EnvBio[R1, E1, B] =
    EnvBio.applyFatal(ctx => runF(ctx).flatMap(f(_).runF(ctx)))

  def >>[R1 <: R, E1 >: E, B](fb: => EnvBio[R1, E1, B]): EnvBio[R1, E1, B] =
    flatMap(_ => fb)

  def <<[R1 <: R, E1 >: E](fb: => EnvBio[R1, E1, Any]): EnvBio[R1, E1, A] =
    flatTap(_ => fb)

  def flatTap[R1 <: R, E1 >: E, B](f: A => EnvBio[R1, E1, B]): EnvBio[R1, E1, A] =
    flatMap(a => f(a).map(_ => a))

  def map2[B, C, R1 <: R, E1 >: E](eb: EnvBio[R1, E1, B])(f: (A, B) => C): EnvBio[R1, E1, C] =
    flatMap(a => eb.map(f(a, _)))

  def map3[B, C, D, R1 <: R, E1 >: E](eb: EnvBio[R1, E1, B], ec: EnvBio[R1, E1, C])(
      f: (A, B, C) => D
  ): EnvBio[R1, E1, D] =
    map2(eb)((a, b) => f(a, b, _)).map2(ec)((fun, c) => fun(c))

  def parMap2[B, C, R1 <: R, E1 >: E](eb: EnvBio[R1, E1, B])(f: (A, B) => C): EnvBio[R1, E1, C] =
    EnvBio.applyFatal(ctx => Task.parMap2(runF(ctx), eb.runF(ctx))(f))

  def parMap3[B, C, D, R1 <: R, E1 >: E](eb: EnvBio[R1, E1, B], ec: EnvBio[R1, E1, C])(
      f: (A, B, C) => D
  ): EnvBio[R1, E1, D] =
    EnvBio.applyFatal(ctx => Task.parMap3(runF(ctx), eb.runF(ctx), ec.runF(ctx))(f))

  def onErrorHandleWith[R1 <: R, E1, A1 >: A](f: E => EnvBio[R1, E1, A1]): EnvBio[R1, E1, A1] =
    EnvBio.applyFatal(ctx =>
      runF(ctx).onErrorHandleWith {
        case UserError(e: E @unchecked) => f(e).runF(ctx)
        case t                          => Task.raiseError(t)
      }
    )

  def onErrorHandle[A1 >: A](f: E => A1): EnvBio[R, Nothing, A1] =
    onErrorHandleWith(e => EnvBio.pure(f(e)))

  def onErrorRecoverWith[R1 <: R, E1 >: E, A2 >: A](f: PartialFunction[E, EnvBio[R1, E1, A2]]): EnvBio[R1, E1, A2] =
    EnvBio.applyFatal(ctx =>
      runF(ctx).onErrorRecoverWith {
        case UserError(e: E @unchecked) if f.isDefinedAt(e) => f(e).runF(ctx)
        case t                                              => Task.raiseError(t)
      }
    )

  def onErrorRecover[A1 >: A](f: PartialFunction[E, A1]): EnvBio[R, E, A1] =
    onErrorRecoverWith(f.andThen(a1 => EnvBio.pure(a1)))

  def mapError[E1](f: E => E1): EnvBio[R, E1, A] =
    onErrorHandleWith(e => EnvBio.raiseError(f(e)))

  def tapError[R1 <: R, E1 >: E, A1 >: A](f: E => EnvBio[R1, E1, A1]): EnvBio[R1, E1, A1] =
    onErrorHandleWith(e => f(e) >> EnvBio.raiseError(e))

  def tapHandle[R1 <: R, E1, A1 >: A](f: E => EnvBio[R1, E1, A1]): EnvBio[R1, E, A1] =
    onErrorHandleWith(e => f(e).mapError(_ => e))

  
  def timed: EnvBio[R, E, (FiniteDuration, A)] = mapTask(_.timed)

  def foldWith[X, B, R1 <: R](
      h: E => EnvBio[R1, X, B],
      f: A => EnvBio[R1, X, B]
  ): EnvBio[R1, X, B] =
    ctx =>
      this.runF(ctx).attempt.flatMap {
        case Left(UserError(e: E @unchecked)) => h(e).runF(ctx)
        case Left(err)                        => Task.raiseError(err)
        case Right(x)                         => f(x).runF(ctx)
      }
}

object EnvBio extends EnvBioFunctions with EnvBioInstances {}

private[bio] final case class UserError(err: Any) extends Throwable 
Example 155
Source File: EnvSuite.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.env

import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import tofu.{HasContextRun, RunContext, WithRun}
import tofu.lift.{Lift, Unlift, UnliftIO}

import scala.concurrent.duration._

class EnvSuite extends AnyFlatSpec with Matchers {
  "flatMap" should "not stack overflow on folding large collection" in {
    (0 to 100000).toList
      .foldLeft(Env.pure[Unit, Int](3)) {
        case (acc, _) => acc.flatMap(Env.pure)
      }
      .run(())
      .runSyncUnsafe(Duration.Inf) shouldBe 3
  }

  "map" should "not stack overflow on folding large collection" in {
    (0 to 100000).toList
      .foldLeft(Env.pure[Unit, Int](3)) {
        case (acc, _) => acc.map(identity)
      }
      .run(())
      .runSyncUnsafe(Duration.Inf) shouldBe 3
  }

  "map2" should "not stack overflow on folding large collection" in {
    (0 to 100000).toList
      .foldLeft(Env.pure[Unit, Int](3)) {
        case (acc, _) => acc.map2(Env.unit[Unit])((a, _) => a)
      }
      .run(())
      .runSyncUnsafe(Duration.Inf) shouldBe 3
  }

  "map3" should "not stack overflow on folding large collection" in {
    (0 to 100000).toList
      .foldLeft(Env.pure[Unit, Int](3)) {
        case (acc, _) => acc.map3(Env.unit[Unit], Env.unit[Unit])((a, _, _) => a)
      }
      .run(())
      .runSyncUnsafe(Duration.Inf) shouldBe 3
  }

  "parMap2" should "not stack overflow on folding large collection" in {
    (0 to 100000).toList
      .foldLeft(Env.pure[Unit, Int](3)) {
        case (acc, _) => acc.parMap2(Env.unit[Unit])((a, _) => a)
      }
      .run(())
      .runSyncUnsafe(Duration.Inf) shouldBe 3
  }

  "parMap3" should "not stack overflow on folding large collection" in {
    (0 to 100000).toList
      .foldLeft(Env.pure[Unit, Int](3)) {
        case (acc, _) => acc.parMap3(Env.unit[Unit], Env.unit[Unit])((a, _, _) => a)
      }
      .run(())
      .runSyncUnsafe(Duration.Inf) shouldBe 3
  }
}

object EnvSuite {
  def summonEnvInstances[E](): Unit = {
    implicitly[Lift[Task, Env[E, *]]]
    implicitly[Unlift[Task, Env[E, *]]]

    implicitly[UnliftIO[Env[E, *]]]

    implicitly[RunContext[Env[E, *]]]
    implicitly[HasContextRun[Env[E, *], Task, E]]
    implicitly[WithRun[Env[E, *], Task, E]]
    ()
  }
} 
Example 156
Source File: TakeWhileInclusiveSuite.scala    From tofu   with Apache License 2.0 5 votes vote down vote up
package tofu.observable

import monix.catnap.MVar
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import monix.reactive.Observable
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

import scala.concurrent.duration._

class TakeWhileInclusiveSuite extends AnyFlatSpec with Matchers {

  private def writeElement[A](mvar: MVar[Task, Vector[A]])(a: A): Task[Unit]                        =
    mvar.take.flatMap(v => mvar.put(v :+ a))
  private def inclusiveElements[A](obs: Observable[A])(p: A => Boolean): Task[(Vector[A], List[A])] =
    for {
      mvar     <- MVar[Task].of(Vector.empty[A])
      produced <- obs.doOnNext(writeElement(mvar)).takeWhileInclusive(p).toListL
      written  <- mvar.read
    } yield (written, produced)

  "Observable.takeWhileInclusibe" should "produce and generate same elements" in {
    inclusiveElements(Observable.range(1, 100))(_ <= 10).runSyncUnsafe(Duration.Inf) shouldEqual
      ((Vector.range(1, 12), List.range(1, 12)))
  }
} 
Example 157
Source File: MonixAsyncHandler.scala    From pulsar4s   with Apache License 2.0 5 votes vote down vote up
package com.sksamuel.pulsar4s.monixs

import java.util.concurrent.CompletableFuture

import com.sksamuel.pulsar4s.{AsyncHandler, ConsumerMessage, DefaultProducer, MessageId, Producer}
import monix.eval.Task
import org.apache.pulsar.client.api
import org.apache.pulsar.client.api.Consumer
import org.apache.pulsar.client.api.{ProducerBuilder, Reader, TypedMessageBuilder}

import scala.compat.java8.FutureConverters
import scala.concurrent.Future
import scala.language.implicitConversions
import scala.util.{Failure, Success, Try}

class MonixAsyncHandler extends AsyncHandler[Task] {

  implicit def completableTToFuture[T](f: => CompletableFuture[T]): Future[T] =
    FutureConverters.toScala(f)

  implicit def completableVoidToTask(f: => CompletableFuture[Void]): Task[Unit] =
    Task.deferFuture(FutureConverters.toScala(f)).map(_ => ())

  override def failed(e: Throwable): Task[Nothing] = Task.raiseError(e)

  override def createProducer[T](builder: ProducerBuilder[T]): Task[Producer[T]] =
    Task.deferFuture(FutureConverters.toScala(builder.createAsync())).map(new DefaultProducer(_))

  override def send[T](t: T, producer: api.Producer[T]): Task[MessageId] = {
    Task.deferFuture {
      val future = producer.sendAsync(t)
      FutureConverters.toScala(future)
    }.map { id => MessageId.fromJava(id) }
  }

  override def receive[T](consumer: api.Consumer[T]): Task[ConsumerMessage[T]] = {
    Task.deferFuture {
      val future = consumer.receiveAsync()
      FutureConverters.toScala(future)
    }.map(ConsumerMessage.fromJava)
  }

  override def getLastMessageId[T](consumer: api.Consumer[T]): Task[MessageId] = {
    Task.deferFuture {
      val future = consumer.getLastMessageIdAsync()
      FutureConverters.toScala(future)
    }.map(MessageId.fromJava)
  }

  def unsubscribeAsync(consumer: api.Consumer[_]): Task[Unit] = consumer.unsubscribeAsync()

  override def close(producer: api.Producer[_]): Task[Unit] = producer.closeAsync()
  override def close(consumer: api.Consumer[_]): Task[Unit] = consumer.closeAsync()

  override def seekAsync(consumer: api.Consumer[_], messageId: MessageId): Task[Unit] =
    consumer.seekAsync(messageId)
  
  override def seekAsync(reader: api.Reader[_], messageId: MessageId): Task[Unit] =
    reader.seekAsync(messageId)
  
  override def seekAsync(reader: api.Reader[_], timestamp: Long): Task[Unit] =
    reader.seekAsync(timestamp)


  override def transform[A, B](t: Task[A])(fn: A => Try[B]): Task[B] =
    t.flatMap { a =>
      fn(a) match {
        case Success(b) => Task.now(b)
        case Failure(e) => Task.raiseError(e)
      }
    }

  override def acknowledgeAsync[T](consumer: api.Consumer[T], messageId: MessageId): Task[Unit] =
    consumer.acknowledgeAsync(messageId)

  override def acknowledgeCumulativeAsync[T](consumer: api.Consumer[T], messageId: MessageId): Task[Unit] =
    consumer.acknowledgeCumulativeAsync(messageId)

  override def negativeAcknowledgeAsync[T](consumer: Consumer[T], messageId: MessageId): Task[Unit] =
    Task { consumer.negativeAcknowledge(messageId) }

  override def close(reader: Reader[_]): Task[Unit] = reader.closeAsync()
  override def flush(producer: api.Producer[_]): Task[Unit] = producer.flushAsync()

  override def nextAsync[T](reader: Reader[T]): Task[ConsumerMessage[T]] =
    Task.deferFuture(reader.readNextAsync()).map(ConsumerMessage.fromJava)

  override def send[T](builder: TypedMessageBuilder[T]): Task[MessageId] =
    Task.deferFuture(builder.sendAsync()).map(MessageId.fromJava)
}

object MonixAsyncHandler {
  implicit def handler: AsyncHandler[Task] = new MonixAsyncHandler
} 
Example 158
Source File: AsyncHttpClientLowLevelMonixWebsocketTest.scala    From sttp   with Apache License 2.0 5 votes vote down vote up
package sttp.client.asynchttpclient.monix

import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.asynchttpclient.ws.{WebSocketListener, WebSocket => AHCWebSocket}
import sttp.client._
import sttp.client.asynchttpclient.WebSocketHandler
import sttp.client.impl.monix.convertMonixTaskToFuture
import sttp.client.testing.ConvertToFuture
import sttp.client.testing.websocket.LowLevelListenerWebSocketTest

class AsyncHttpClientLowLevelMonixWebsocketTest
    extends LowLevelListenerWebSocketTest[Task, AHCWebSocket, WebSocketHandler] {
  override implicit val backend: SttpBackend[Task, Nothing, WebSocketHandler] =
    AsyncHttpClientMonixBackend().runSyncUnsafe()
  override implicit val convertToFuture: ConvertToFuture[Task] = convertMonixTaskToFuture

  override def createHandler(_onTextFrame: String => Unit): WebSocketHandler[AHCWebSocket] =
    WebSocketHandler.fromListener(new WebSocketListener {
      override def onOpen(websocket: AHCWebSocket): Unit = {}
      override def onClose(websocket: AHCWebSocket, code: Int, reason: String): Unit = {}
      override def onError(t: Throwable): Unit = {}
      override def onTextFrame(payload: String, finalFragment: Boolean, rsv: Int): Unit = {
        _onTextFrame(payload)
      }
    })

  override def sendText(ws: AHCWebSocket, t: String): Unit = ws.sendTextFrame(t).await()

  override def sendCloseFrame(ws: AHCWebSocket): Unit = ws.sendCloseFrame()
} 
Example 159
Source File: AsyncHttpClientMonixHttpTest.scala    From sttp   with Apache License 2.0 5 votes vote down vote up
package sttp.client.asynchttpclient.monix

import java.util.concurrent.TimeoutException

import monix.eval.Task
import sttp.client._
import sttp.client.impl.monix.convertMonixTaskToFuture
import sttp.client.testing.{CancelTest, ConvertToFuture, HttpTest}
import monix.execution.Scheduler.Implicits.global

import scala.concurrent.duration._

class AsyncHttpClientMonixHttpTest extends HttpTest[Task] with CancelTest[Task, Nothing] {
  override implicit val backend: SttpBackend[Task, Nothing, NothingT] = AsyncHttpClientMonixBackend().runSyncUnsafe()
  override implicit val convertToFuture: ConvertToFuture[Task] = convertMonixTaskToFuture

  override def timeoutToNone[T](t: Task[T], timeoutMillis: Int): Task[Option[T]] =
    t.map(Some(_))
      .timeout(timeoutMillis.milliseconds)
      .onErrorRecover {
        case _: TimeoutException => None
      }

  override def throwsExceptionOnUnsupportedEncoding = false
} 
Example 160
Source File: AsyncHttpClientHighLevelMonixWebsocketTest.scala    From sttp   with Apache License 2.0 5 votes vote down vote up
package sttp.client.asynchttpclient.monix

import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import sttp.client._
import sttp.client.asynchttpclient.{AsyncHttpClientHighLevelWebsocketTest, WebSocketHandler}
import sttp.client.impl.monix.{TaskMonadAsyncError, convertMonixTaskToFuture}
import sttp.client.monad.MonadError
import sttp.client.testing.ConvertToFuture
import sttp.client.ws.WebSocket

import scala.concurrent.duration._

class AsyncHttpClientHighLevelMonixWebsocketTest extends AsyncHttpClientHighLevelWebsocketTest[Task] {
  override implicit val backend: SttpBackend[Task, Nothing, WebSocketHandler] =
    AsyncHttpClientMonixBackend().runSyncUnsafe()
  override implicit val convertToFuture: ConvertToFuture[Task] = convertMonixTaskToFuture
  override implicit val monad: MonadError[Task] = TaskMonadAsyncError

  override def createHandler: Option[Int] => Task[WebSocketHandler[WebSocket[Task]]] = MonixWebSocketHandler(_)

  override def eventually[T](interval: FiniteDuration, attempts: Int)(f: => Task[T]): Task[T] = {
    (Task.sleep(interval) >> f).onErrorRestart(attempts.toLong)
  }
} 
Example 161
Source File: PostSerializeJsonMonixAsyncHttpClientCirce.scala    From sttp   with Apache License 2.0 5 votes vote down vote up
package sttp.client.examples

object PostSerializeJsonMonixAsyncHttpClientCirce extends App {
  import sttp.client._
  import sttp.client.circe._
  import sttp.client.asynchttpclient.monix._
  import io.circe.generic.auto._
  import monix.eval.Task

  case class Info(x: Int, y: String)

  val postTask = AsyncHttpClientMonixBackend().flatMap { implicit backend =>
    val r = basicRequest
      .body(Info(91, "abc"))
      .post(uri"https://httpbin.org/post")

    r.send()
      .flatMap { response => Task(println(s"""Got ${response.code} response, body:\n${response.body}""")) }
      .guarantee(backend.close())
  }

  import monix.execution.Scheduler.Implicits.global
  postTask.runSyncUnsafe()
} 
Example 162
Source File: WebsocketMonix.scala    From sttp   with Apache License 2.0 5 votes vote down vote up
package sttp.client.examples

import monix.eval.Task
import sttp.client._
import sttp.client.ws.{WebSocket, WebSocketResponse}
import sttp.model.ws.WebSocketFrame
import sttp.client.asynchttpclient.monix.{AsyncHttpClientMonixBackend, MonixWebSocketHandler}

object WebsocketMonix extends App {
  import monix.execution.Scheduler.Implicits.global

  def useWebsocket(ws: WebSocket[Task]): Task[Unit] = {
    def send(i: Int) = ws.send(WebSocketFrame.text(s"Hello $i!"))
    val receive = ws.receiveText().flatMap(t => Task(println(s"RECEIVED: $t")))
    send(1) *> send(2) *> receive *> receive *> ws.close
  }

  val websocketTask: Task[Unit] = AsyncHttpClientMonixBackend().flatMap { implicit backend =>
    val response: Task[WebSocketResponse[WebSocket[Task]]] = basicRequest
      .get(uri"wss://echo.websocket.org")
      .openWebsocketF(MonixWebSocketHandler())

    response
      .flatMap(r => useWebsocket(r.result))
      .guarantee(backend.close())
  }

  websocketTask.runSyncUnsafe()
} 
Example 163
Source File: MonixWebSocketHandler.scala    From sttp   with Apache License 2.0 5 votes vote down vote up
package sttp.client.httpclient.monix

import monix.eval.Task
import monix.execution.Scheduler
import sttp.client.httpclient.WebSocketHandler
import sttp.client.httpclient.internal.NativeWebSocketHandler
import sttp.client.impl.monix.{MonixAsyncQueue, TaskMonadAsyncError}
import sttp.client.ws.{WebSocket, WebSocketEvent}

object MonixWebSocketHandler {
  val IncomingBufferCapacity = 2 // has to be at least 2 (opening frame + one data frame)

  
  def apply()(implicit
      s: Scheduler
  ): Task[WebSocketHandler[WebSocket[Task]]] = {
    Task {
      val queue =
        new MonixAsyncQueue[WebSocketEvent](Some(IncomingBufferCapacity))
      NativeWebSocketHandler(queue, TaskMonadAsyncError)
    }
  }
} 
Example 164
Source File: HttpClientMonixBackend.scala    From sttp   with Apache License 2.0 5 votes vote down vote up
package sttp.client.httpclient.monix

import java.io.InputStream
import java.net.http.HttpRequest.BodyPublishers
import java.net.http.{HttpClient, HttpRequest}
import java.nio.ByteBuffer

import cats.effect.Resource
import monix.eval.Task
import monix.execution.Scheduler
import monix.reactive.Observable
import org.reactivestreams.FlowAdapters
import sttp.client.httpclient.HttpClientBackend.EncodingHandler
import sttp.client.httpclient.{HttpClientAsyncBackend, HttpClientBackend, WebSocketHandler}
import sttp.client.impl.monix.TaskMonadAsyncError
import sttp.client.testing.SttpBackendStub
import sttp.client.{FollowRedirectsBackend, SttpBackend, SttpBackendOptions}

import scala.util.{Success, Try}

class HttpClientMonixBackend private (
    client: HttpClient,
    closeClient: Boolean,
    customizeRequest: HttpRequest => HttpRequest,
    customEncodingHandler: EncodingHandler
)(implicit s: Scheduler)
    extends HttpClientAsyncBackend[Task, Observable[ByteBuffer]](
      client,
      TaskMonadAsyncError,
      closeClient,
      customizeRequest,
      customEncodingHandler
    ) {
  override def streamToRequestBody(stream: Observable[ByteBuffer]): Task[HttpRequest.BodyPublisher] = {
    monad.eval(BodyPublishers.fromPublisher(FlowAdapters.toFlowPublisher(stream.toReactivePublisher)))
  }

  override def responseBodyToStream(responseBody: InputStream): Try[Observable[ByteBuffer]] = {
    Success(
      Observable
        .fromInputStream(Task.now(responseBody))
        .map(ByteBuffer.wrap)
        .guaranteeCase(_ => Task(responseBody.close()))
    )
  }
}

object HttpClientMonixBackend {
  private def apply(
      client: HttpClient,
      closeClient: Boolean,
      customizeRequest: HttpRequest => HttpRequest,
      customEncodingHandler: EncodingHandler
  )(implicit
      s: Scheduler
  ): SttpBackend[Task, Observable[ByteBuffer], WebSocketHandler] =
    new FollowRedirectsBackend(
      new HttpClientMonixBackend(client, closeClient, customizeRequest, customEncodingHandler)(s)
    )

  def apply(
      options: SttpBackendOptions = SttpBackendOptions.Default,
      customizeRequest: HttpRequest => HttpRequest = identity,
      customEncodingHandler: EncodingHandler = PartialFunction.empty
  )(implicit
      s: Scheduler = Scheduler.global
  ): Task[SttpBackend[Task, Observable[ByteBuffer], WebSocketHandler]] =
    Task.eval(
      HttpClientMonixBackend(
        HttpClientBackend.defaultClient(options),
        closeClient = true,
        customizeRequest,
        customEncodingHandler
      )(s)
    )

  def resource(
      options: SttpBackendOptions = SttpBackendOptions.Default,
      customizeRequest: HttpRequest => HttpRequest = identity,
      customEncodingHandler: EncodingHandler = PartialFunction.empty
  )(implicit
      s: Scheduler = Scheduler.global
  ): Resource[Task, SttpBackend[Task, Observable[ByteBuffer], WebSocketHandler]] =
    Resource.make(apply(options, customizeRequest, customEncodingHandler))(_.close())

  def usingClient(
      client: HttpClient,
      customizeRequest: HttpRequest => HttpRequest = identity,
      customEncodingHandler: EncodingHandler = PartialFunction.empty
  )(implicit s: Scheduler = Scheduler.global): SttpBackend[Task, Observable[ByteBuffer], WebSocketHandler] =
    HttpClientMonixBackend(client, closeClient = false, customizeRequest, customEncodingHandler)(s)

  
  def stub: SttpBackendStub[Task, Observable[ByteBuffer], WebSocketHandler] = SttpBackendStub(TaskMonadAsyncError)
} 
Example 165
Source File: HttpClientHighLevelMonixWebsocketTest.scala    From sttp   with Apache License 2.0 5 votes vote down vote up
package sttp.client.httpclient.monix

import java.nio.ByteBuffer

import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import monix.reactive.Observable
import sttp.client._
import sttp.client.httpclient.WebSocketHandler
import sttp.client.impl.monix.{TaskMonadAsyncError, convertMonixTaskToFuture}
import sttp.client.monad.MonadError
import sttp.client.testing.ConvertToFuture
import sttp.client.testing.websocket.HighLevelWebsocketTest
import sttp.client.ws.WebSocket
import sttp.client.testing.HttpTest.wsEndpoint

import scala.concurrent.duration._

class HttpClientHighLevelMonixWebsocketTest extends HighLevelWebsocketTest[Task, WebSocketHandler] {
  implicit val backend: SttpBackend[Task, Observable[ByteBuffer], WebSocketHandler] =
    HttpClientMonixBackend().runSyncUnsafe()
  implicit val convertToFuture: ConvertToFuture[Task] = convertMonixTaskToFuture
  implicit val monad: MonadError[Task] = TaskMonadAsyncError

  override def createHandler: Option[Int] => Task[WebSocketHandler[WebSocket[Task]]] = _ => MonixWebSocketHandler()

  it should "handle backpressure correctly" in {
    basicRequest
      .get(uri"$wsEndpoint/ws/echo")
      .openWebsocketF(createHandler(None))
      .flatMap { response =>
        val ws = response.result
        send(ws, 1000) >> eventually(10.millis, 500) { ws.isOpen.map(_ shouldBe true) }
      }
      .toFuture()
  }

  override def eventually[T](interval: FiniteDuration, attempts: Int)(f: => Task[T]): Task[T] = {
    (Task.sleep(interval) >> f).onErrorRestart(attempts.toLong)
  }
} 
Example 166
Source File: OkHttpHighLevelMonixWebsocketTest.scala    From sttp   with Apache License 2.0 5 votes vote down vote up
package sttp.client.okhttp.monix

import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.scalatest.Assertion
import sttp.client._
import sttp.client.impl.monix.{TaskMonadAsyncError, convertMonixTaskToFuture}
import sttp.client.monad.MonadError
import sttp.client.monad.syntax._
import sttp.client.okhttp.WebSocketHandler
import sttp.client.okhttp.monix.internal.SendMessageException
import sttp.client.testing.ConvertToFuture
import sttp.client.testing.websocket.HighLevelWebsocketTest
import sttp.client.ws.WebSocket
import sttp.client.testing.HttpTest.wsEndpoint

import scala.concurrent.duration._

class OkHttpHighLevelMonixWebsocketTest extends HighLevelWebsocketTest[Task, WebSocketHandler] {
  override implicit val backend: SttpBackend[Task, Nothing, WebSocketHandler] =
    OkHttpMonixBackend().runSyncUnsafe()
  override implicit val convertToFuture: ConvertToFuture[Task] = convertMonixTaskToFuture
  override implicit val monad: MonadError[Task] = TaskMonadAsyncError

  override def createHandler: Option[Int] => Task[WebSocketHandler[WebSocket[Task]]] = MonixWebSocketHandler(_)

  it should "error if the endpoint is not a websocket" in {
    monad
      .handleError {
        basicRequest
          .get(uri"$wsEndpoint/echo")
          .openWebsocketF(createHandler(None))
          .map(_ => fail: Assertion)
      } {
        case e: Exception => (e shouldBe a[SttpClientException.ReadException]).unit
      }
      .toFuture()
  }

  it should "error if incoming messages overflow the buffer" in {
    basicRequest
      .get(uri"$wsEndpoint/ws/echo")
      .openWebsocketF(createHandler(Some(3)))
      .flatMap { response =>
        val ws = response.result
        send(ws, 1000) >> eventually(10 millis, 400)(ws.isOpen.map(_ shouldBe false))
      }
      .onErrorRecover {
        case _: SendMessageException => succeed
      }
      .toFuture()
  }

  override def eventually[T](interval: FiniteDuration, attempts: Int)(f: => Task[T]): Task[T] = {
    (Task.sleep(interval) >> f).onErrorRestart(attempts)
  }
} 
Example 167
Source File: MonixAsyncQueue.scala    From sttp   with Apache License 2.0 5 votes vote down vote up
package sttp.client.impl.monix

import monix.eval.Task
import sttp.model.ws.WebSocketBufferFull
import monix.execution.{Scheduler, AsyncQueue => MAsyncQueue}
import sttp.client.ws.internal.AsyncQueue

class MonixAsyncQueue[A](bufferCapacity: Option[Int])(implicit s: Scheduler) extends AsyncQueue[Task, A] {
  private val queue = bufferCapacity match {
    case Some(capacity) => MAsyncQueue.bounded[A](capacity)
    case None           => MAsyncQueue.unbounded[A]()
  }

  override def offer(t: A): Unit = {
    if (!queue.tryOffer(t)) {
      throw new WebSocketBufferFull()
    }
  }
  override def poll: Task[A] = Task.deferFuture(queue.poll())
} 
Example 168
Source File: TaskMonadAsyncError.scala    From sttp   with Apache License 2.0 5 votes vote down vote up
package sttp.client.impl.monix

import monix.eval.Task
import sttp.client.monad.{Canceler, MonadAsyncError}

import scala.util.{Failure, Success}

object TaskMonadAsyncError extends MonadAsyncError[Task] {
  override def unit[T](t: T): Task[T] = Task.now(t)

  override def map[T, T2](fa: Task[T])(f: (T) => T2): Task[T2] = fa.map(f)

  override def flatMap[T, T2](fa: Task[T])(f: (T) => Task[T2]): Task[T2] =
    fa.flatMap(f)

  override def async[T](register: (Either[Throwable, T] => Unit) => Canceler): Task[T] =
    Task.cancelable { cb =>
      val canceler = register {
        case Left(t)  => cb(Failure(t))
        case Right(t) => cb(Success(t))
      }
      Task(canceler.cancel())
    }

  override def error[T](t: Throwable): Task[T] = Task.raiseError(t)

  override protected def handleWrappedError[T](rt: Task[T])(h: PartialFunction[Throwable, Task[T]]): Task[T] =
    rt.onErrorRecoverWith(h)

  override def eval[T](t: => T): Task[T] = Task(t)
} 
Example 169
Source File: MonixStreamingTest.scala    From sttp   with Apache License 2.0 5 votes vote down vote up
package sttp.client.impl.monix

import java.nio.ByteBuffer

import monix.eval.Task
import monix.reactive.Observable
import sttp.client.testing.ConvertToFuture
import sttp.client.testing.streaming.StreamingTest

abstract class MonixStreamingTest extends StreamingTest[Task, Observable[ByteBuffer]] {

  override implicit val convertToFuture: ConvertToFuture[Task] = convertMonixTaskToFuture

  override def bodyProducer(chunks: Iterable[Array[Byte]]): Observable[ByteBuffer] =
    Observable
      .fromIterable(chunks)
      .map(ByteBuffer.wrap)

  override def bodyConsumer(stream: Observable[ByteBuffer]): Task[String] =
    stream
      .flatMap(v => Observable.fromIterable(v.array()))
      .toListL
      .map(bs => new String(bs.toArray, "utf8"))
} 
Example 170
Source File: MonixMtlInstances.scala    From meow-mtl   with MIT License 5 votes vote down vote up
package com.olegpy.meow.internal

import cats.Semigroup
import cats.syntax.semigroup._
import cats.{Applicative, Functor, Monad}
import cats.mtl.{ApplicativeLocal, DefaultFunctorTell, DefaultMonadState}
import monix.eval.TaskLocal
import monix.eval.Task

private[meow] object MonixMtlInstances {
  class TaskLocalApplicativeLocal[E](taskLocal: TaskLocal[E])
    extends ApplicativeLocal[Task, E] {
    override def local[A](f: E => E)(fa: Task[A]): Task[A] =
      taskLocal.bindL(taskLocal.read map f)(fa)

    override def scope[A](e: E)(fa: Task[A]): Task[A] = taskLocal.bind(e)(fa)
    override val applicative: Applicative[Task] = Task.catsAsync
    override def ask: Task[E] = taskLocal.read
    override def reader[A](f: E => A): Task[A] = ask.map(f)
  }

  class TaskLocalMonadState[S](taskLocal: TaskLocal[S])
    extends DefaultMonadState[Task, S] {
    override val monad: Monad[Task] = Task.catsAsync
    override def get: Task[S] = taskLocal.read
    override def set(s: S): Task[Unit] = taskLocal.write(s)
  }

  class TaskLocalFunctorTell[E: Semigroup](taskLocal: TaskLocal[E])
    extends DefaultFunctorTell[Task, E] {
    override val functor: Functor[Task] = Task.catsAsync
    override def tell(l: E): Task[Unit] =
      taskLocal.read.flatMap(e => taskLocal.write(e |+| l))
  }
} 
Example 171
Source File: RouteGuideMonixService.scala    From grpcexample   with MIT License 5 votes vote down vote up
package io.grpc.routeguide

import java.util.concurrent.TimeUnit.NANOSECONDS
import java.util.logging.Logger

import concurrency.AtomicRef
import monix.eval.Task
import monix.reactive.Observable

class RouteGuideMonixService(features: Seq[Feature]) extends RouteGuideGrpcMonix.RouteGuide {

  val logger: Logger = Logger.getLogger(classOf[RouteGuideMonixService].getName)

  private val routeNotes: AtomicRef[Map[Point, Seq[RouteNote]]] = new AtomicRef(Map.empty)

  
  override def routeChat(notes: Observable[RouteNote]): Observable[RouteNote] =
    notes.flatMap { note =>
      addNote(note)
      Observable.fromIterable(getNotes(note.getLocation))
    }

  private def findFeature(point: Point): Feature = {
    features.find { feature =>
      feature.getLocation.latitude == point.latitude && feature.getLocation.longitude == point.longitude
    } getOrElse new Feature(location = Some(point))
  }

  private def getNotes(point: Point): Seq[RouteNote] = {
    routeNotes.get.getOrElse(point, Seq.empty)
  }

  private def addNote(note: RouteNote): Unit = {
    routeNotes.updateAndGet { notes =>
      val existingNotes = notes.getOrElse(note.getLocation, Seq.empty)
      val updatedNotes = existingNotes :+ note
      notes + (note.getLocation -> updatedNotes)
    }
  }
} 
Example 172
Source File: MonixStreams.scala    From neotypes   with MIT License 5 votes vote down vote up
package neotypes.monix.stream

import monix.eval.Task
import monix.reactive.Observable

trait MonixStreams {
  implicit final val monixStream: neotypes.Stream.Aux[Observable, Task] =
    new neotypes.Stream[Observable] {
      override final type F[T] = Task[T]

      override final def init[T](value: () => Task[Option[T]]): Observable[T] =
        Observable
          .repeatEvalF(Task.suspend(value()))
          .takeWhile(option => option.isDefined)
          .collect { case Some(t) => t }

      override final def onComplete[T](s: Observable[T])(f: => Task[Unit]): Observable[T] =
        s.guarantee(f)

      override final def fToS[T](f: Task[Observable[T]]): Observable[T] =
        Observable.fromTask(f).flatten
    }
} 
Example 173
Source File: Monix.scala    From neotypes   with MIT License 5 votes vote down vote up
package neotypes.monix

import cats.effect.{ExitCase, Resource}
import monix.eval.Task

trait Monix {
  implicit final def monixAsync: neotypes.Async.Aux[Task, Monix.TaskResource] =
    Monix.instance
}

object Monix {
  private[neotypes] final type TaskResource[A] = Resource[Task, A]

  private final val instance: neotypes.Async.Aux[Task, TaskResource] =
    new neotypes.Async[Task] {
      override final type R[A] = Resource[Task, A]

      override final def async[T](cb: (Either[Throwable, T] => Unit) => Unit): Task[T] =
        Task.async(cb)

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

      override final def failed[T](e: Throwable): Task[T] =
        Task.raiseError(e)

      override final def flatMap[T, U](m: Task[T])(f: T => Task[U]): Task[U] =
        m.flatMap(f)

      override final def guarantee[A, B](fa: Task[A])
                                        (f: A => Task[B])
                                        (finalizer: (A, Option[Throwable]) => Task[Unit]): Task[B] =
        Resource.makeCase(fa) {
          case (a, ExitCase.Completed | ExitCase.Canceled) => finalizer(a, None)
          case (a, ExitCase.Error(ex))                     => finalizer(a, Some(ex))
        }.use(f)

      override final def map[T, U](m: Task[T])(f: T => U): Task[U] =
        m.map(f)

      override final def recoverWith[T, U >: T](m: Task[T])(f: PartialFunction[Throwable, Task[U]]): Task[U] =
        m.onErrorRecoverWith(f)

      override final def resource[A](input: => A)(close: A => Task[Unit]): Resource[Task, A] =
        Resource.make(Task.delay(input))(close)
    }
} 
Example 174
Source File: GrpcMonix.scala    From grpcmonix   with MIT License 5 votes vote down vote up
package grpcmonix

import com.google.common.util.concurrent.ListenableFuture
import io.grpc.stub.StreamObserver
import monix.eval.{Callback, Task}
import monix.execution.Ack.{Continue, Stop}
import monix.execution.{Ack, Scheduler}
import monix.reactive.Observable
import monix.reactive.observables.ObservableLike.{Operator, Transformer}
import monix.reactive.observers.Subscriber
import monix.reactive.subjects.PublishSubject
import org.reactivestreams.{Subscriber => SubscriberR}
import scalapb.grpc.Grpc

import scala.concurrent.Future

object GrpcMonix {

  type GrpcOperator[I, O] = StreamObserver[O] => StreamObserver[I]

  def guavaFutureToMonixTask[T](future: ListenableFuture[T]): Task[T] =
    Task.deferFuture {
      Grpc.guavaFuture2ScalaFuture(future)
    }

  def grpcOperatorToMonixOperator[I,O](grpcOperator: GrpcOperator[I,O]): Operator[I,O] = {
    outputSubsriber: Subscriber[O] =>
      val outputObserver: StreamObserver[O] = monixSubscriberToGrpcObserver(outputSubsriber)
      val inputObserver: StreamObserver[I] = grpcOperator(outputObserver)
      grpcObserverToMonixSubscriber(inputObserver, outputSubsriber.scheduler)
  }

  def monixSubscriberToGrpcObserver[T](subscriber: Subscriber[T]): StreamObserver[T] =
    new StreamObserver[T] {
      override def onError(t: Throwable): Unit = subscriber.onError(t)
      override def onCompleted(): Unit = subscriber.onComplete()
      override def onNext(value: T): Unit = subscriber.onNext(value)
    }

  def reactiveSubscriberToGrpcObserver[T](subscriber: SubscriberR[_ >: T]): StreamObserver[T] =
    new StreamObserver[T] {
      override def onError(t: Throwable): Unit = subscriber.onError(t)
      override def onCompleted(): Unit = subscriber.onComplete()
      override def onNext(value: T): Unit = subscriber.onNext(value)
    }

  def grpcObserverToMonixSubscriber[T](observer: StreamObserver[T], s: Scheduler): Subscriber[T] =
    new Subscriber[T] {
      override implicit def scheduler: Scheduler = s
      override def onError(t: Throwable): Unit = observer.onError(t)
      override def onComplete(): Unit = observer.onCompleted()
      override def onNext(value: T): Future[Ack] =
        try {
          observer.onNext(value)
          Continue
        } catch {
          case t: Throwable =>
            observer.onError(t)
            Stop
        }
    }

  def grpcObserverToMonixCallback[T](observer: StreamObserver[T]): Callback[T] =
    new Callback[T] {
      override def onError(t: Throwable): Unit = observer.onError(t)
      override def onSuccess(value: T): Unit = {
        observer.onNext(value)
        observer.onCompleted()
      }
    }

  def liftByGrpcOperator[I, O](observable: Observable[I], operator: GrpcOperator[I, O]): Observable[O] =
    observable.liftByOperator(
      grpcOperatorToMonixOperator(operator)
    )

  def unliftByTransformer[I, O](transformer: Transformer[I, O], subscriber: Subscriber[O]): Subscriber[I] =
    new Subscriber[I] {
      private[this] val subject = PublishSubject[I]()
      subject.transform(transformer).subscribe(subscriber)

      override implicit def scheduler: Scheduler = subscriber.scheduler
      override def onError(t: Throwable): Unit = subject.onError(t)
      override def onComplete(): Unit = subject.onComplete()
      override def onNext(value: I): Future[Ack] = subject.onNext(value)
    }

} 
Example 175
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 176
Source File: DestinationInfoHttpEndpointSpec.scala    From core   with Apache License 2.0 5 votes vote down vote up
package com.smartbackpackerapp.http

import cats.Parallel
import cats.syntax.option._
import com.smartbackpackerapp.common.TaskAssertion
import com.smartbackpackerapp.config.SBConfiguration
import com.smartbackpackerapp.http.Http4sUtils._
import com.smartbackpackerapp.model.{Country, CountryCode, CountryName, Currency, VisaNotRequired, VisaRequirementsData}
import com.smartbackpackerapp.repository.algebra.VisaRequirementsRepository
import com.smartbackpackerapp.service.{AbstractExchangeRateService, CurrencyExchangeDTO, DestinationInfoService}
import monix.eval.Task
import org.http4s.{HttpService, Query, Request, Status, Uri}
import org.scalatest.prop.PropertyChecks
import org.scalatest.{FlatSpecLike, Matchers}

class DestinationInfoHttpEndpointSpec extends FlatSpecLike with Matchers with DestinationInfoHttpEndpointFixture {

  forAll(examples) { (from, to, expectedStatus, expectedCountry, expectedVisa) =>
    it should s"retrieve visa requirements from $from to $to" in TaskAssertion {
      val request = Request[Task](uri = Uri(path = s"/$ApiVersion/traveling/$from/to/$to", query = Query(("baseCurrency", Some("EUR")))))

      httpService(request).value.map { task =>
        task.fold(fail("Empty response")){ response =>
          response.status should be (expectedStatus)

          val body = response.body.asString
          assert(body.contains(expectedCountry))
          assert(body.contains(expectedVisa))
        }
      }
    }
  }

}

trait DestinationInfoHttpEndpointFixture extends PropertyChecks {

  val examples = Table(
    ("from", "code", "expectedStatus","expectedCountry", "expectedVisa"),
    ("AR", "GB", Status.Ok, "United Kingdom", "VisaNotRequired"),
    ("AR", "KO", Status.NotFound, "Country not found", """{"code":"100","error":"Country not found KO"}"""),
    ("AR", "AR", Status.BadRequest, "Countries must be different", """{"code":"101","error":"Countries must be different!"}""")
  )

  private val repo = new VisaRequirementsRepository[Task] {
    override def findVisaRequirements(from: CountryCode, to: CountryCode): Task[Option[VisaRequirementsData]] = Task {
      if (to.value == "KO") none[VisaRequirementsData]
      else
      VisaRequirementsData(
        from = Country(CountryCode("AR"), CountryName("Argentina"), Currency("ARS")),
        to   = Country(CountryCode("GB"), CountryName("United Kingdom"), Currency("GBP")),
        visaCategory = VisaNotRequired,
        description = "90 days within any 180 day period"
      ).some
    }
  }

  private lazy val sbConfig = new SBConfiguration[Task]

  private val rateService = new AbstractExchangeRateService[Task](sbConfig) {
    override protected def retrieveExchangeRate(uri: String): Task[CurrencyExchangeDTO] = Task {
      CurrencyExchangeDTO("EUR", "", Map("RON" -> 4.59))
    }
  }

  private implicit val errorHandler = new HttpErrorHandler[Task]

  private implicit val parallel: Parallel[Task, Task] =
    Parallel[Task, Task.Par].asInstanceOf[Parallel[Task, Task]]

  val httpService: HttpService[Task] =
    taskMiddleware(
      new DestinationInfoHttpEndpoint(
        new DestinationInfoService[Task](sbConfig, repo, rateService)
      ).service
    )

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

import cats.{Applicative, Monad}
import cats.data.{Kleisli, OptionT}
import cats.effect.IO
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.http4s.server.AuthMiddleware
import org.http4s.{EntityBody, Request}

import scala.concurrent.Await
import scala.concurrent.duration.Duration

object Http4sUtils {

  private def authUser[F[_]](implicit F: Applicative[F]): Kleisli[OptionT[F, ?], Request[F], String] =
    Kleisli(_ => OptionT.liftF(F.pure("access_token")))

  def middleware[F[_]: Monad]: AuthMiddleware[F, String] = AuthMiddleware.apply[F, String](authUser)

  val taskMiddleware: AuthMiddleware[Task, String] = middleware[Task]
  val ioMiddleware: AuthMiddleware[IO, String] = middleware[IO]

  implicit class ByteVector2String(body: EntityBody[IO]) {
    def asString: String = {
      val array = body.compile.toVector.unsafeRunSync().toArray
      new String(array.map(_.toChar))
    }
  }

  implicit class ByteVector2StringTask(body: EntityBody[Task]) {
    def asString: String = {
      val array = Await.result(body.compile.toVector.runAsync, Duration.Inf).toArray
      new String(array.map(_.toChar))
    }
  }

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

import cats.Parallel
import cats.data.EitherT
import cats.syntax.option._
import com.smartbackpackerapp.common.TaskAssertion
import com.smartbackpackerapp.config.SBConfiguration
import com.smartbackpackerapp.model._
import com.smartbackpackerapp.repository.algebra.VisaRequirementsRepository
import monix.eval.Task
import org.scalatest.{FlatSpecLike, Matchers}

class DestinationInfoServiceSpec extends FlatSpecLike with Matchers {

  private lazy val sbConfig = new SBConfiguration[Task]

  private val repo = new VisaRequirementsRepository[Task] {
    override def findVisaRequirements(from: CountryCode, to: CountryCode): Task[Option[VisaRequirementsData]] = Task {
      VisaRequirementsData(
        from = Country(CountryCode("AR"), CountryName("Argentina"), Currency("ARS")),
        to   = Country(CountryCode("RO"), CountryName("Romania"), Currency("RON")),
        visaCategory = VisaNotRequired,
        description = "90 days within any 180 day period"
      ).some
    }
  }

  private val rateService = new AbstractExchangeRateService[Task](sbConfig) {
    override protected def retrieveExchangeRate(uri: String): Task[CurrencyExchangeDTO] = Task {
      CurrencyExchangeDTO("EUR", "", Map("RON" -> 4.59))
    }
  }

  private implicit val parallel: Parallel[Task, Task] =
    Parallel[Task, Task.Par].asInstanceOf[Parallel[Task, Task]]

  private val service = new DestinationInfoService[Task](sbConfig, repo, rateService)

  it should "retrieve destination information" in TaskAssertion {
    EitherT(service.find(CountryCode("AR"), CountryCode("RO"), Currency("EUR"))).map { info =>
      info.countryCode.value  should be ("RO")
      info.countryName.value  should be ("Romania")
      info.exchangeRate       should be (ExchangeRate(Currency("EUR"), Currency("RON"), 4.59))
      info.visaRequirements   should be (VisaRequirements(VisaNotRequired, "90 days within any 180 day period"))
    }.value
  }

  it should "validate countries" in TaskAssertion {
    EitherT(service.find(CountryCode("AR"), CountryCode("AR"), Currency("EUR"))).leftMap { error =>
      error should be (CountriesMustBeDifferent)
    }.value
  }

} 
Example 179
Source File: MonixExample.scala    From korolev   with Apache License 2.0 5 votes vote down vote up
import korolev.Context
import korolev.akka._
import korolev.server.{KorolevServiceConfig, StateLoader}
import korolev.state.javaSerialization._
import korolev.monix._

import monix.eval.Task
import monix.execution.Scheduler.Implicits.global

object MonixExample extends SimpleAkkaHttpKorolevApp {

  val ctx = Context[Task, Option[String], Any]

  import ctx._
  import levsha.dsl._
  import html._

  private val aInput = elementId()
  private val bInput = elementId()

  def service: AkkaHttpService = akkaHttpService {
    KorolevServiceConfig[Task, Option[String], Any](
      stateLoader = StateLoader.default(None),
      document = maybeResult => optimize {
        Html(
          body(
            form(
              input(aInput, `type` := "number", event("input")(onChange)),
              span("+"),
              input(bInput, `type` := "number", event("input")(onChange)),
              span("="),
              maybeResult.map(result => span(result)),
            )
          )
        )
      }
    )
  }

  private def onChange(access: Access) =
    for {
      a <- access.valueOf(aInput)
      b <- access.valueOf(bInput)
      _ <-
        if (a.trim.isEmpty || b.trim.isEmpty) Task.unit
        else access.transition(_ => Some((a.toInt + b.toInt).toString))
    } yield ()
} 
Example 180
Source File: TagRoutes.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.routes

import akka.http.scaladsl.model.StatusCodes.{Created, OK}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import ch.epfl.bluebrain.nexus.admin.client.types.Project
import ch.epfl.bluebrain.nexus.iam.client.types._
import ch.epfl.bluebrain.nexus.kg.config.AppConfig
import ch.epfl.bluebrain.nexus.kg.config.Contexts.tagCtxUri
import ch.epfl.bluebrain.nexus.kg.config.Vocabulary.nxv
import ch.epfl.bluebrain.nexus.kg.directives.AuthDirectives._
import ch.epfl.bluebrain.nexus.kg.directives.ProjectDirectives._
import ch.epfl.bluebrain.nexus.kg.marshallers.instances._
import ch.epfl.bluebrain.nexus.kg.resources._
import ch.epfl.bluebrain.nexus.kg.resources.syntax._
import ch.epfl.bluebrain.nexus.rdf.Iri.AbsoluteIri
import ch.epfl.bluebrain.nexus.rdf.implicits._
import io.circe.syntax._
import io.circe.{Encoder, Json}
import kamon.Kamon
import kamon.instrumentation.akka.http.TracingDirectives.operationName
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global

class TagRoutes private[routes] (resourceType: String, tags: Tags[Task], schema: Ref, write: Permission)(
    implicit acls: AccessControlLists,
    caller: Caller,
    project: Project,
    config: AppConfig
) {

  
  def routes(id: AbsoluteIri): Route =
    // Consume the tag segment
    pathPrefix("tags") {
      concat(
        // Create tag
        (post & parameter("rev".as[Long]) & pathEndOrSingleSlash) { rev =>
          operationName(opName) {
            (hasPermission(write) & projectNotDeprecated) {
              entity(as[Json]) { source =>
                Kamon.currentSpan().tag("resource.operation", "create")
                complete(tags.create(Id(project.ref, id), rev, source, schema).value.runWithStatus(Created))
              }
            }
          }
        },
        // Fetch a tag
        (get & projectNotDeprecated & pathEndOrSingleSlash) {
          operationName(opName) {
            hasPermission(read).apply {
              parameter("rev".as[Long].?) {
                case Some(rev) => complete(tags.fetch(Id(project.ref, id), rev, schema).value.runWithStatus(OK))
                case _         => complete(tags.fetch(Id(project.ref, id), schema).value.runWithStatus(OK))
              }
            }
          }
        }
      )
    }

  private implicit def tagsEncoder: Encoder[TagSet] =
    Encoder.instance(tags => Json.obj(nxv.tags.prefix -> Json.arr(tags.map(_.asJson).toSeq: _*)).addContext(tagCtxUri))

  private def opName: String = resourceType match {
    case "resources" => s"/${config.http.prefix}/resources/{org}/{project}/{schemaId}/{id}/tags"
    case _           => s"/${config.http.prefix}/$resourceType/{org}/{project}/{id}/tags"
  }
} 
Example 181
Source File: Clients.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.routes

import ch.epfl.bluebrain.nexus.admin.client.AdminClient
import ch.epfl.bluebrain.nexus.commons.es.client.ElasticSearchClient
import ch.epfl.bluebrain.nexus.commons.http.HttpClient
import ch.epfl.bluebrain.nexus.commons.http.HttpClient.UntypedHttpClient
import ch.epfl.bluebrain.nexus.commons.search.QueryResults
import ch.epfl.bluebrain.nexus.commons.sparql.client.BlazegraphClient
import ch.epfl.bluebrain.nexus.iam.client.IamClient
import ch.epfl.bluebrain.nexus.storage.client.StorageClient
import io.circe.Json
import monix.eval.Task


final case class Clients[F[_]]()(
    implicit val sparql: BlazegraphClient[F],
    val elasticSearch: ElasticSearchClient[F],
    val admin: AdminClient[F],
    val iam: IamClient[F],
    val defaultRemoteStorage: StorageClient[F],
    val rsSearch: HttpClient[F, QueryResults[Json]],
    val http: UntypedHttpClient[Task],
    val uclJson: HttpClient[Task, Json]
)

object Clients {
  implicit def esClient[F[_]](implicit clients: Clients[F]): ElasticSearchClient[F]  = clients.elasticSearch
  implicit def sparqlClient[F[_]](implicit clients: Clients[F]): BlazegraphClient[F] = clients.sparql
} 
Example 182
Source File: ArchiveRoutes.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.routes

import akka.http.scaladsl.model.StatusCodes.{Created, OK}
import akka.http.scaladsl.model.headers.Accept
import akka.http.scaladsl.model.{HttpEntity, MediaTypes}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import ch.epfl.bluebrain.nexus.admin.client.types.Project
import ch.epfl.bluebrain.nexus.iam.client.types._
import ch.epfl.bluebrain.nexus.kg.KgError.{InvalidOutputFormat, UnacceptedResponseContentType}
import ch.epfl.bluebrain.nexus.kg.archives.Archive._
import ch.epfl.bluebrain.nexus.kg.config.AppConfig
import ch.epfl.bluebrain.nexus.kg.directives.AuthDirectives.hasPermission
import ch.epfl.bluebrain.nexus.kg.directives.PathDirectives._
import ch.epfl.bluebrain.nexus.kg.directives.ProjectDirectives._
import ch.epfl.bluebrain.nexus.kg.directives.QueryDirectives.outputFormat
import ch.epfl.bluebrain.nexus.kg.marshallers.instances._
import ch.epfl.bluebrain.nexus.kg.resources._
import ch.epfl.bluebrain.nexus.kg.resources.syntax._
import ch.epfl.bluebrain.nexus.kg.routes.OutputFormat.Tar
import ch.epfl.bluebrain.nexus.rdf.Iri.AbsoluteIri
import io.circe.Json
import kamon.instrumentation.akka.http.TracingDirectives.operationName
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global

class ArchiveRoutes private[routes] (archives: Archives[Task])(
    implicit acls: AccessControlLists,
    project: Project,
    caller: Caller,
    config: AppConfig
) {

  private val responseType = MediaTypes.`application/x-tar`

  
  def routes(id: AbsoluteIri): Route = {
    val resId = Id(project.ref, id)
    concat(
      // Create archive
      (put & pathEndOrSingleSlash) {
        operationName(s"/${config.http.prefix}/archives/{org}/{project}/{id}") {
          (hasPermission(write) & projectNotDeprecated) {
            entity(as[Json]) { source =>
              complete(archives.create(resId, source).value.runWithStatus(Created))
            }
          }
        }
      },
      // Fetch archive
      (get & outputFormat(strict = true, Tar) & pathEndOrSingleSlash) {
        case Tar                           => getArchive(resId)
        case format: NonBinaryOutputFormat => getResource(resId)(format)
        case other                         => failWith(InvalidOutputFormat(other.toString))

      }
    )
  }

  private def getResource(resId: ResId)(implicit format: NonBinaryOutputFormat): Route =
    completeWithFormat(archives.fetch(resId).value.runWithStatus(OK))

  private def getArchive(resId: ResId): Route = {
    parameter("ignoreNotFound".as[Boolean] ? false) { ignoreNotFound =>
      onSuccess(archives.fetchArchive(resId, ignoreNotFound).value.runToFuture) {
        case Right(source) =>
          headerValueByType[Accept](()) { accept =>
            if (accept.mediaRanges.exists(_.matches(responseType)))
              complete(HttpEntity(responseType, source))
            else
              failWith(
                UnacceptedResponseContentType(
                  s"File Media Type '$responseType' does not match the Accept header value '${accept.mediaRanges.mkString(", ")}'"
                )
              )
          }
        case Left(err) => complete(err)
      }
    }
  }
}

object ArchiveRoutes {
  final def apply(archives: Archives[Task])(
      implicit acls: AccessControlLists,
      caller: Caller,
      project: Project,
      config: AppConfig
  ): ArchiveRoutes = new ArchiveRoutes(archives)
} 
Example 183
Source File: Status.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.routes

import akka.Done
import akka.actor.ActorSystem
import akka.cluster.{Cluster, MemberStatus}
import akka.event.Logging
import akka.persistence.cassandra.CassandraPluginConfig
import akka.persistence.cassandra.session.scaladsl.CassandraSession
import ch.epfl.bluebrain.nexus.kg.config.AppConfig.PersistenceConfig
import monix.eval.Task

import scala.concurrent.Future

sealed trait Status {

  
  def check: Task[Boolean]
}

object Status {

  class CassandraStatus(implicit as: ActorSystem, persistence: PersistenceConfig) extends Status {
    implicit val ec     = as.dispatcher
    private val log     = Logging(as, "CassandraHeathCheck")
    private val config  = new CassandraPluginConfig(as, as.settings.config.getConfig(persistence.journalPlugin))
    private val (p, s)  = (config.sessionProvider, config.sessionSettings)
    private val session = new CassandraSession(as, p, s, ec, log, "health", _ => Future.successful(Done.done()))
    private val query   = s"SELECT now() FROM ${config.keyspace}.messages;"

    override def check: Task[Boolean] =
      Task.deferFuture(session.selectOne(query).map(_ => true).recover {
        case err =>
          log.error("Error while attempting to query for health check", err)
          false
      })
  }

  class ClusterStatus(cluster: Cluster) extends Status {
    override def check: Task[Boolean] =
      Task.pure(
        !cluster.isTerminated &&
          cluster.state.leader.isDefined && cluster.state.members.nonEmpty &&
          !cluster.state.members.exists(_.status != MemberStatus.Up) && cluster.state.unreachable.isEmpty
      )
  }
} 
Example 184
Source File: MigrateV12ToV13.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg

import akka.actor.ActorSystem
import akka.persistence.cassandra.query.scaladsl.CassandraReadJournal
import akka.persistence.query.{EventEnvelope, NoOffset, PersistenceQuery}
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.client.AdminClient
import ch.epfl.bluebrain.nexus.admin.client.types.Project
import ch.epfl.bluebrain.nexus.commons.test.Resources
import ch.epfl.bluebrain.nexus.iam.client.types._
import ch.epfl.bluebrain.nexus.kg.config.AppConfig
import ch.epfl.bluebrain.nexus.kg.config.Vocabulary.nxv
import ch.epfl.bluebrain.nexus.kg.resources.Event.{Created, Updated}
import ch.epfl.bluebrain.nexus.kg.resources.{OrganizationRef, ResId, Views}
import com.typesafe.scalalogging.Logger
import io.circe.Json
import io.circe.parser.parse
import monix.eval.Task
import monix.execution.Scheduler
import monix.execution.schedulers.CanBlock

import scala.concurrent.Future

object MigrateV12ToV13 extends Resources {
  private val log                                     = Logger[MigrateV12ToV13.type]
  private val newMapping                              = jsonContentOf("/elasticsearch/mapping.json")
  private val defaultEsId                             = nxv.defaultElasticSearchIndex.value
  private implicit val mockedAcls: AccessControlLists = AccessControlLists.empty

  def migrate(
      views: Views[Task],
      adminClient: AdminClient[Task]
  )(implicit config: AppConfig, as: ActorSystem, sc: Scheduler, pm: CanBlock): Unit = {

    implicit val token: Option[AuthToken] = config.iam.serviceAccountToken

    def checkAndUpdateMapping(id: ResId, rev: Long, source: Json)(
        implicit project: Project,
        caller: Caller
    ): Task[Unit] = {

      source.hcursor.get[String]("mapping").flatMap(parse) match {
        case Left(err) =>
          log.error(s"Error while fetching mapping for view id ${id.show}. Reason: '$err'")
          Task.unit
        case Right(mapping) if mapping == newMapping =>
          Task.unit
        case _ =>
          views.update(id, rev, source deepMerge Json.obj("mapping" -> newMapping)).value.flatMap {
            case Left(err) =>
              log.error(s"Error updating the view with id '${id.show}' and rev '$rev'. Reason: '$err'")
              Task.unit
            case _ =>
              log.info(s"View with id '${id.show}' and rev '$rev' was successfully updated.")
              Task.unit
          }
      }
    }

    def fetchProject(orgRef: OrganizationRef, id: ResId)(f: Project => Task[Unit]): Task[Unit] = {
      adminClient.fetchProject(orgRef.id, id.parent.id).flatMap {
        case Some(project) => f(project)
        case None =>
          log.error(s"Project with id '${id.parent.id}' was not found for view with id '${id.show}'")
          Task.unit

      }
    }

    log.info("Migrating views mappings.")
    val pq = PersistenceQuery(as).readJournalFor[CassandraReadJournal](CassandraReadJournal.Identifier)
    Task
      .fromFuture {
        pq.currentEventsByTag(s"type=${nxv.ElasticSearchView.value.asString}", NoOffset)
          .mapAsync(1) {
            case EventEnvelope(_, _, _, Created(id, orgRef, _, _, source, _, subject)) if id.value == defaultEsId =>
              fetchProject(orgRef, id) { project =>
                checkAndUpdateMapping(id, 1L, source)(project, Caller(subject, Set(subject)))
              }.runToFuture
            case EventEnvelope(_, _, _, Updated(id, orgRef, rev, _, source, _, subject)) if id.value == defaultEsId =>
              fetchProject(orgRef, id) { project =>
                checkAndUpdateMapping(id, rev, source)(project, Caller(subject, Set(subject)))
              }.runToFuture
            case _ =>
              Future.unit

          }
          .runFold(0) {
            case (acc, _) =>
              if (acc % 10 == 0) log.info(s"Processed '$acc' persistence ids.")
              acc + 1
          }
          .map(_ => ())
      }
      .runSyncUnsafe()
    log.info("Finished migrating views mappings.")
  }

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

import akka.http.scaladsl.model.headers.OAuth2BearerToken
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.directives.FutureDirectives.onComplete
import akka.http.scaladsl.server.{Directive0, Directive1}
import ch.epfl.bluebrain.nexus.admin.client.types.Project
import ch.epfl.bluebrain.nexus.iam.client.types._
import ch.epfl.bluebrain.nexus.iam.client.{IamClient, IamClientError}
import ch.epfl.bluebrain.nexus.kg.KgError.{AuthenticationFailed, AuthorizationFailed, InternalError}
import ch.epfl.bluebrain.nexus.kg.resources.syntax._
import com.typesafe.scalalogging.Logger
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global

import scala.util.{Failure, Success}

object AuthDirectives {

  private val logger = Logger[this.type]

  
  def extractCaller(implicit iam: IamClient[Task], token: Option[AuthToken]): Directive1[Caller] =
    onComplete(iam.identities.runToFuture).flatMap {
      case Success(caller)                         => provide(caller)
      case Failure(_: IamClientError.Unauthorized) => failWith(AuthenticationFailed)
      case Failure(_: IamClientError.Forbidden)    => failWith(AuthorizationFailed)
      case Failure(err) =>
        val message = "Error when trying to extract the subject"
        logger.error(message, err)
        failWith(InternalError(message))
    }
} 
Example 186
Source File: ProjectAttributesCoordinator.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.async

import akka.actor.{ActorRef, ActorSystem}
import cats.effect.Async
import cats.implicits._
import ch.epfl.bluebrain.nexus.admin.client.types.Project
import ch.epfl.bluebrain.nexus.kg.async.ProjectAttributesCoordinatorActor.Msg._
import ch.epfl.bluebrain.nexus.kg.cache.ProjectCache
import ch.epfl.bluebrain.nexus.kg.config.AppConfig
import ch.epfl.bluebrain.nexus.kg.resources.ProjectIdentifier.ProjectRef
import ch.epfl.bluebrain.nexus.kg.resources.syntax._
import ch.epfl.bluebrain.nexus.kg.resources.{Files, OrganizationRef}
import ch.epfl.bluebrain.nexus.kg.storage.Storage.StorageOperations.FetchAttributes
import ch.epfl.bluebrain.nexus.sourcing.projections.Projections
import monix.eval.Task


  def stop(projectRef: ProjectRef): F[Unit] = {
    ref ! Stop(projectRef.id)
    F.unit
  }
}

object ProjectAttributesCoordinator {
  def apply(files: Files[Task], projectCache: ProjectCache[Task])(
      implicit config: AppConfig,
      fetchAttributes: FetchAttributes[Task],
      as: ActorSystem,
      P: Projections[Task, String]
  ): ProjectAttributesCoordinator[Task] = {
    val coordinatorRef = ProjectAttributesCoordinatorActor.start(files, None, config.cluster.shards)
    new ProjectAttributesCoordinator[Task](projectCache, coordinatorRef)
  }
} 
Example 187
Source File: RepairFromMessages.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg

import java.net.URLDecoder
import java.util.UUID

import akka.actor.ActorSystem
import akka.persistence.cassandra.query.scaladsl.CassandraReadJournal
import akka.persistence.query.PersistenceQuery
import ch.epfl.bluebrain.nexus.kg.resources.{Id, Repo, ResId}
import ch.epfl.bluebrain.nexus.kg.resources.ProjectIdentifier.ProjectRef
import ch.epfl.bluebrain.nexus.rdf.Iri
import com.typesafe.scalalogging.Logger
import monix.eval.Task
import monix.execution.Scheduler
import monix.execution.schedulers.CanBlock

import scala.concurrent.Future
import scala.util.Try


object RepairFromMessages {
  // $COVERAGE-OFF$

  private val log = Logger[RepairFromMessages.type]

  def repair(repo: Repo[Task])(implicit as: ActorSystem, sc: Scheduler, pm: CanBlock): Unit = {
    log.info("Repairing dependent tables from messages.")
    val pq = PersistenceQuery(as).readJournalFor[CassandraReadJournal](CassandraReadJournal.Identifier)
    Task
      .fromFuture {
        pq.currentPersistenceIds()
          .mapAsync(1) {
            case ResourceId(id) => (repo.get(id, None).value >> Task.unit).runToFuture
            case other =>
              log.warn(s"Unknown persistence id '$other'")
              Future.successful(())
          }
          .runFold(0) {
            case (acc, _) =>
              if (acc % 1000 == 0) log.info(s"Processed '$acc' persistence ids.")
              acc + 1
          }
          .map(_ => ())
      }
      .runSyncUnsafe()
    log.info("Finished repairing dependent tables from messages.")
  }

  object ResourceId {
    private val regex =
      "^resources\\-([0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12})\\-(.+)$".r
    def unapply(arg: String): Option[ResId] =
      arg match {
        case regex(stringUuid, stringId) =>
          for {
            uuid <- Try(UUID.fromString(stringUuid)).toOption
            iri  <- Iri.absolute(URLDecoder.decode(stringId, "UTF-8")).toOption
          } yield Id(ProjectRef(uuid), iri)
        case _ => None
      }
  }
  // $COVERAGE-ON$
} 
Example 188
Source File: ResolverCacheSpec.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.ExtendedActorSystem
import akka.serialization.Serialization
import akka.testkit._
import ch.epfl.bluebrain.nexus.commons.test.ActorSystemFixture
import ch.epfl.bluebrain.nexus.iam.client.types.Identity.Anonymous
import ch.epfl.bluebrain.nexus.kg.TestHelper
import ch.epfl.bluebrain.nexus.kg.config.AppConfig._
import ch.epfl.bluebrain.nexus.kg.config.{AppConfig, Settings}
import ch.epfl.bluebrain.nexus.kg.resolve.Resolver._
import ch.epfl.bluebrain.nexus.kg.resources.ProjectIdentifier.{ProjectLabel, ProjectRef}
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.matchers.should.Matchers
import org.scalatest.{Inspectors, TryValues}

import scala.concurrent.duration._

//noinspection NameBooleanParameters
class ResolverCacheSpec
    extends ActorSystemFixture("ResolverCacheSpec", true)
    with Matchers
    with Inspectors
    with ScalaFutures
    with TryValues
    with TestHelper {

  override implicit def patienceConfig: PatienceConfig = PatienceConfig(3.seconds.dilated, 5.milliseconds)

  private implicit val appConfig: AppConfig = Settings(system).appConfig

  val ref1 = ProjectRef(genUUID)
  val ref2 = ProjectRef(genUUID)

  val label1 = ProjectLabel(genString(), genString())
  val label2 = ProjectLabel(genString(), genString())

  val resolver: InProjectResolver = InProjectResolver(ref1, genIri, 1L, false, 10)
  val crossRefs: CrossProjectResolver =
    CrossProjectResolver(Set(genIri), List(ref1, ref2), Set(Anonymous), ref1, genIri, 0L, false, 1)
  val crossLabels: CrossProjectResolver =
    CrossProjectResolver(Set(genIri), List(label1, label2), Set(Anonymous), ref1, genIri, 0L, false, 1)

  val resolverProj1: Set[InProjectResolver] = List.fill(5)(resolver.copy(id = genIri)).toSet
  val resolverProj2: Set[InProjectResolver] = List.fill(5)(resolver.copy(id = genIri, ref = ref2)).toSet

  private val cache = ResolverCache[Task]

  "ResolverCache" should {

    "index resolvers" in {
      val list = (resolverProj1 ++ resolverProj2).toList
      forAll(list) { resolver =>
        cache.put(resolver).runToFuture.futureValue
        cache.get(resolver.ref, resolver.id).runToFuture.futureValue shouldEqual Some(resolver)
      }
    }

    "list resolvers" in {
      cache.get(ref1).runToFuture.futureValue should contain theSameElementsAs resolverProj1
      cache.get(ref2).runToFuture.futureValue should contain theSameElementsAs resolverProj2
    }

    "deprecate resolver" in {
      val resolver = resolverProj1.head
      cache.put(resolver.copy(deprecated = true, rev = 2L)).runToFuture.futureValue
      cache.get(resolver.ref, resolver.id).runToFuture.futureValue shouldEqual None
      cache.get(ref1).runToFuture.futureValue should contain theSameElementsAs resolverProj1.filterNot(_ == resolver)
    }

    "serialize cross project resolver" when {
      val serialization = new Serialization(system.asInstanceOf[ExtendedActorSystem])
      "parameterized with ProjectRef" in {
        val bytes = serialization.serialize(crossRefs).success.value
        val out   = serialization.deserialize(bytes, classOf[CrossProjectResolver]).success.value
        out shouldEqual crossRefs
      }
      "parameterized with ProjectLabel" in {
        val bytes = serialization.serialize(crossLabels).success.value
        val out   = serialization.deserialize(bytes, classOf[CrossProjectResolver]).success.value
        out shouldEqual crossLabels
      }
    }
  }
} 
Example 189
Source File: StorageCacheSpec.scala    From nexus-kg   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.kg.cache

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

import akka.testkit._
import ch.epfl.bluebrain.nexus.commons.test.ActorSystemFixture
import ch.epfl.bluebrain.nexus.kg.TestHelper
import ch.epfl.bluebrain.nexus.kg.config.AppConfig._
import ch.epfl.bluebrain.nexus.kg.config.{AppConfig, Settings}
import ch.epfl.bluebrain.nexus.kg.resources.ProjectIdentifier.{ProjectRef}
import ch.epfl.bluebrain.nexus.kg.storage.Storage.DiskStorage
import ch.epfl.bluebrain.nexus.rdf.implicits._
import monix.eval.Task
import monix.execution.Scheduler.Implicits.global
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.matchers.should.Matchers
import org.scalatest.{Inspectors, TryValues}

import scala.concurrent.duration._

//noinspection NameBooleanParameters
class StorageCacheSpec
    extends ActorSystemFixture("StorageCacheSpec", true)
    with Matchers
    with Inspectors
    with ScalaFutures
    with TryValues
    with TestHelper {

  override implicit def patienceConfig: PatienceConfig = PatienceConfig(3.seconds.dilated, 5.milliseconds)

  private implicit val clock: Clock         = Clock.systemUTC
  private implicit val appConfig: AppConfig = Settings(system).appConfig

  val ref1 = ProjectRef(genUUID)
  val ref2 = ProjectRef(genUUID)

  val time   = clock.instant()
  val lastId = url"http://example.com/lastA"
  // initialInstant.minusSeconds(1L + genInt().toLong)

  val tempStorage = DiskStorage(ref1, genIri, 1L, false, true, "alg", Paths.get("/tmp"), read, write, 1024L)

  val lastStorageProj1 = tempStorage.copy(id = lastId)
  val lastStorageProj2 = tempStorage.copy(ref = ref2, id = lastId)

  val storagesProj1: List[DiskStorage] = List.fill(5)(tempStorage.copy(id = genIri)) :+ lastStorageProj1
  val storagesProj2: List[DiskStorage] = List.fill(5)(tempStorage.copy(ref = ref2, id = genIri)) :+ lastStorageProj2

  private val cache = StorageCache[Task]

  "StorageCache" should {

    "index storages" in {
      forAll((storagesProj1 ++ storagesProj2).zipWithIndex) {
        case (storage, index) =>
          implicit val instant = time.plusSeconds(index.toLong)
          cache.put(storage).runToFuture.futureValue
          cache.get(storage.ref, storage.id).runToFuture.futureValue shouldEqual Some(storage)
      }
    }

    "get latest default storage" in {
      cache.getDefault(ref1).runToFuture.futureValue shouldEqual Some(lastStorageProj1)
      cache.getDefault(ref2).runToFuture.futureValue shouldEqual Some(lastStorageProj2)
      cache.getDefault(ProjectRef(genUUID)).runToFuture.futureValue shouldEqual None
    }

    "list storages" in {
      cache.get(ref1).runToFuture.futureValue should contain theSameElementsAs storagesProj1
      cache.get(ref2).runToFuture.futureValue should contain theSameElementsAs storagesProj2
    }

    "deprecate storage" in {
      val storage          = storagesProj1.head
      implicit val instant = time.plusSeconds(30L)
      cache.put(storage.copy(deprecated = true, rev = 2L)).runToFuture.futureValue
      cache.get(storage.ref, storage.id).runToFuture.futureValue shouldEqual None
      cache.get(ref1).runToFuture.futureValue should contain theSameElementsAs storagesProj1.filterNot(_ == storage)
    }
  }
} 
Example 190
Source File: Signed.scala    From Waves   with MIT License 5 votes vote down vote up
package com.wavesplatform.transaction

import com.wavesplatform.transaction.TxValidationError.InvalidSignature
import monix.eval.{Coeval, Task}
import monix.execution.Scheduler
import monix.execution.schedulers.SchedulerService

import scala.concurrent.Await
import scala.concurrent.duration.Duration

trait Signed extends Authorized {
  protected val signatureValid: Coeval[Boolean]

  protected val signedDescendants: Coeval[Seq[Signed]] =
    Coeval(Nil)

  protected val signaturesValidMemoized: Task[Either[InvalidSignature, this.type]] =
    Signed.validateTask[this.type](this).memoize

  val signaturesValid: Coeval[Either[InvalidSignature, this.type]] =
    Coeval.evalOnce(Await.result(signaturesValidMemoized.runToFuture(Signed.scheduler), Duration.Inf))
}

object Signed {
  type E[A] = Either[InvalidSignature, A]

  private implicit lazy val scheduler: SchedulerService = {
    val parallelism = (Runtime.getRuntime.availableProcessors() / 2).max(1).min(4)
    Scheduler.computation(parallelism, "sig-validator")
  }

  def validateOrdered[S <: Signed](ss: Seq[S]): E[Seq[S]] =
    Await.result(
      Task
        .parTraverse(ss)(s => s.signaturesValidMemoized)
        .map(
          _.collectFirst { case Left(v) => Left(v) }.getOrElse(Right(ss))
        )
        .runAsyncLogErr,
      Duration.Inf
    )

  private def validateTask[S <: Signed](signedEntity: S): Task[E[S]] =
    Task {
      import cats.instances.either._
      import cats.instances.list._
      import cats.syntax.traverse._

      if (!signedEntity.signatureValid()) {
        Task.now(Left(InvalidSignature(signedEntity, None)))
      } else if (signedEntity.signedDescendants().isEmpty) {
        Task.now(Right(signedEntity))
      } else {
        Task
          .parTraverseUnordered(signedEntity.signedDescendants())(s => s.signaturesValidMemoized)
          .map(_.sequence.map(_ => signedEntity))
      }
    }.flatten
} 
Example 191
Source File: BalanceDistribution.scala    From Waves   with MIT License 5 votes vote down vote up
package com.wavesplatform.api.common

import com.google.common.collect.AbstractIterator
import com.google.common.primitives.{Ints, Longs}
import com.wavesplatform.account.Address
import com.wavesplatform.database.{AddressId, DBExt, DBResource, Keys}
import com.wavesplatform.state.Portfolio
import com.wavesplatform.state.Portfolio.longSemigroup
import monix.eval.Task
import monix.reactive.Observable
import org.iq80.leveldb.DB

import scala.annotation.tailrec
import scala.jdk.CollectionConverters._

trait BalanceDistribution {
  import BalanceDistribution._
  def balanceDistribution(
      db: DB,
      height: Int,
      after: Option[Address],
      overrides: Map[Address, Portfolio],
      globalPrefix: Array[Byte],
      addressId: Array[Byte] => AddressId,
      balanceOf: Portfolio => Long
  ): Observable[(Address, Long)] =
    db.resourceObservable
      .flatMap { resource =>
        resource.iterator.seek(
          globalPrefix ++ after.flatMap(address => resource.get(Keys.addressId(address))).fold(Array.emptyByteArray)(id => Longs.toByteArray(id.toLong + 1))
        )
        Observable.fromIterator(Task(new BalanceIterator(resource, globalPrefix, addressId, balanceOf, height, overrides).asScala.filter(_._2 > 0)))
      }

}

object BalanceDistribution {
  class BalanceIterator(
      resource: DBResource,
      globalPrefix: Array[Byte],
      addressId: Array[Byte] => AddressId,
      balanceOf: Portfolio => Long,
      height: Int,
      private var pendingPortfolios: Map[Address, Portfolio]
  ) extends AbstractIterator[(Address, Long)] {
    @inline
    private def stillSameAddress(expected: AddressId): Boolean = resource.iterator.hasNext && {
      val maybeNext = resource.iterator.peekNext().getKey
      maybeNext.startsWith(globalPrefix) && addressId(maybeNext) == expected
    }
    @tailrec
    private def findNextBalance(): Option[(Address, Long)] = {
      if (!resource.iterator.hasNext) None
      else {
        val current = resource.iterator.next()
        if (!current.getKey.startsWith(globalPrefix)) None
        else {
          val aid           = addressId(current.getKey)
          val address       = resource.get(Keys.idToAddress(aid))
          var balance       = Longs.fromByteArray(current.getValue)
          var currentHeight = Ints.fromByteArray(current.getKey.takeRight(4))

          while (stillSameAddress(aid)) {
            val next       = resource.iterator.next()
            val nextHeight = Ints.fromByteArray(next.getKey.takeRight(4))
            if (nextHeight <= height) {
              currentHeight = nextHeight
              balance = Longs.fromByteArray(next.getValue)
            }
          }

          pendingPortfolios -= address
          val adjustedBalance = longSemigroup.combine(balance, pendingPortfolios.get(address).fold(0L)(balanceOf))

          if (currentHeight <= height && adjustedBalance > 0) Some(address -> adjustedBalance)
          else findNextBalance()
        }
      }
    }

    override def computeNext(): (Address, Long) = findNextBalance() match {
      case Some(balance) => balance
      case None =>
        if (pendingPortfolios.nonEmpty) {
          val (address, portfolio) = pendingPortfolios.head
          pendingPortfolios -= address
          address -> balanceOf(portfolio)
        } else {
          endOfData()
        }
    }
  }
} 
Example 192
Source File: ScorexLogging.scala    From Waves   with MIT License 5 votes vote down vote up
package com.wavesplatform.utils

import monix.eval.Task
import monix.execution.{CancelableFuture, Scheduler}
import monix.reactive.Observable
import org.slf4j.{Logger, LoggerFactory}

case class LoggerFacade(logger: Logger) {
  def trace(message: => String, throwable: Throwable): Unit = {
    if (logger.isTraceEnabled)
      logger.trace(message, throwable)
  }

  def trace(message: => String): Unit = {
    if (logger.isTraceEnabled)
      logger.trace(message)
  }

  def debug(message: => String, arg: Any): Unit = {
    if (logger.isDebugEnabled)
      logger.debug(message, arg)
  }

  def debug(message: => String): Unit = {
    if (logger.isDebugEnabled)
      logger.debug(message)
  }

  def info(message: => String): Unit = {
    if (logger.isInfoEnabled)
      logger.info(message)
  }

  def info(message: => String, arg: Any): Unit = {
    if (logger.isInfoEnabled)
      logger.info(message, arg)
  }

  def info(message: => String, throwable: Throwable): Unit = {
    if (logger.isInfoEnabled)
      logger.info(message, throwable)
  }

  def warn(message: => String): Unit = {
    if (logger.isWarnEnabled)
      logger.warn(message)
  }

  def warn(message: => String, throwable: Throwable): Unit = {
    if (logger.isWarnEnabled)
      logger.warn(message, throwable)
  }

  def error(message: => String): Unit = {
    if (logger.isErrorEnabled)
      logger.error(message)
  }

  def error(message: => String, throwable: Throwable): Unit = {
    if (logger.isErrorEnabled)
      logger.error(message, throwable)
  }
}

trait ScorexLogging {
  protected lazy val log = LoggerFacade(LoggerFactory.getLogger(this.getClass))

  implicit class TaskExt[A](t: Task[A]) {
    def runAsyncLogErr(implicit s: Scheduler): CancelableFuture[A] =
      logErr.runToFuture(s)

    def logErr: Task[A] = {
      t.onErrorHandleWith(ex => {
        log.error(s"Error executing task", ex)
        Task.raiseError[A](ex)
      })
    }
  }

  implicit class ObservableExt[A](o: Observable[A]) {

    def logErr: Observable[A] = {
      o.onErrorHandleWith(ex => {
        log.error(s"Error observing item", ex)
        Observable.raiseError[A](ex)
      })
    }
  }
} 
Example 193
Source File: Time.scala    From Waves   with MIT License 5 votes vote down vote up
package com.wavesplatform.utils

import java.net.{InetAddress, SocketTimeoutException}

import monix.eval.Task
import monix.execution.ExecutionModel
import monix.execution.schedulers.SchedulerService
import org.apache.commons.net.ntp.NTPUDPClient

import scala.concurrent.duration.DurationInt

trait Time {
  def correctedTime(): Long

  def getTimestamp(): Long
}

class NTP(ntpServer: String) extends Time with ScorexLogging with AutoCloseable {

  log.info("Initializing time")

  private val offsetPanicThreshold = 1000000L
  private val ExpirationTimeout    = 60.seconds
  private val RetryDelay           = 10.seconds
  private val ResponseTimeout      = 10.seconds

  private implicit val scheduler: SchedulerService =
    Schedulers.singleThread(name = "time-impl", reporter = log.error("Error in NTP", _), ExecutionModel.AlwaysAsyncExecution)

  private val client = new NTPUDPClient()
  client.setDefaultTimeout(ResponseTimeout.toMillis.toInt)

  @volatile private var offset = 0L
  private val updateTask: Task[Unit] = {
    def newOffsetTask: Task[Option[(InetAddress, java.lang.Long)]] = Task {
      try {
        client.open()
        val info = client.getTime(InetAddress.getByName(ntpServer))
        info.computeDetails()
        Option(info.getOffset).map { offset =>
          val r = if (Math.abs(offset) > offsetPanicThreshold) throw new Exception("Offset is suspiciously large") else offset
          (info.getAddress, r)
        }
      } catch {
        case _: SocketTimeoutException =>
          None
        case t: Throwable =>
          log.warn("Problems with NTP: ", t)
          None
      } finally {
        client.close()
      }
    }

    newOffsetTask.flatMap {
      case None if !scheduler.isShutdown => updateTask.delayExecution(RetryDelay)
      case Some((server, newOffset)) if !scheduler.isShutdown =>
        log.trace(s"Adjusting time with $newOffset milliseconds, source: ${server.getHostAddress}.")
        offset = newOffset
        updateTask.delayExecution(ExpirationTimeout)
      case _ => Task.unit
    }
  }

  def correctedTime(): Long = System.currentTimeMillis() + offset

  private var txTime: Long = 0

  def getTimestamp(): Long = {
    txTime = Math.max(correctedTime(), txTime + 1)
    txTime
  }

  private val taskHandle = updateTask.runAsyncLogErr

  override def close(): Unit = {
    log.info("Shutting down Time")
    taskHandle.cancel()
    scheduler.shutdown()
  }
} 
Example 194
Source File: MicroblockAppender.scala    From Waves   with MIT License 5 votes vote down vote up
package com.wavesplatform.state.appender

import cats.data.EitherT
import com.wavesplatform.block.Block.BlockId
import com.wavesplatform.block.MicroBlock
import com.wavesplatform.lang.ValidationError
import com.wavesplatform.metrics.{BlockStats, _}
import com.wavesplatform.network.MicroBlockSynchronizer.MicroblockData
import com.wavesplatform.network._
import com.wavesplatform.state.Blockchain
import com.wavesplatform.transaction.BlockchainUpdater
import com.wavesplatform.transaction.TxValidationError.InvalidSignature
import com.wavesplatform.utils.ScorexLogging
import com.wavesplatform.utx.UtxPool
import io.netty.channel.Channel
import io.netty.channel.group.ChannelGroup
import kamon.Kamon
import monix.eval.Task
import monix.execution.Scheduler

import scala.util.{Left, Right}

object MicroblockAppender extends ScorexLogging {
  def apply(blockchainUpdater: BlockchainUpdater with Blockchain, utxStorage: UtxPool, scheduler: Scheduler, verify: Boolean = true)(
      microBlock: MicroBlock
  ): Task[Either[ValidationError, BlockId]] = {

    Task(metrics.microblockProcessingTimeStats.measureSuccessful {
      blockchainUpdater
        .processMicroBlock(microBlock, verify)
        .map { totalBlockId =>
          utxStorage.removeAll(microBlock.transactionData)
          totalBlockId
        }
    }).executeOn(scheduler)
  }

  def apply(
      blockchainUpdater: BlockchainUpdater with Blockchain,
      utxStorage: UtxPool,
      allChannels: ChannelGroup,
      peerDatabase: PeerDatabase,
      scheduler: Scheduler
  )(ch: Channel, md: MicroblockData): Task[Unit] = {
    import md.microBlock
    val microblockTotalResBlockSig = microBlock.totalResBlockSig
    (for {
      _ <- EitherT(Task.now(microBlock.signaturesValid()))
      _ <- EitherT(apply(blockchainUpdater, utxStorage, scheduler)(microBlock))
    } yield ()).value.map {
      case Right(_) =>
        md.invOpt match {
          case Some(mi) => allChannels.broadcast(mi, except = md.microblockOwners())
          case None     => log.warn(s"${id(ch)} Not broadcasting MicroBlockInv")
        }
        BlockStats.applied(microBlock)
      case Left(is: InvalidSignature) =>
        peerDatabase.blacklistAndClose(ch, s"Could not append microblock $microblockTotalResBlockSig: $is")
      case Left(ve) =>
        BlockStats.declined(microBlock)
        log.debug(s"${id(ch)} Could not append microblock $microblockTotalResBlockSig: $ve")
    }
  }

  private[this] object metrics {
    val microblockProcessingTimeStats = Kamon.timer("microblock-appender.processing-time").withoutTags()
  }
} 
Example 195
Source File: Context.scala    From Waves   with MIT License 5 votes vote down vote up
package com.wavesplatform.extensions

import akka.actor.ActorSystem
import com.wavesplatform.account.Address
import com.wavesplatform.api.common._
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.events.{BlockchainUpdated, UtxEvent}
import com.wavesplatform.lang.ValidationError
import com.wavesplatform.settings.WavesSettings
import com.wavesplatform.state.Blockchain
import com.wavesplatform.transaction.smart.script.trace.TracedResult
import com.wavesplatform.transaction.{Asset, DiscardedBlocks, Transaction}
import com.wavesplatform.utils.Time
import com.wavesplatform.utx.UtxPool
import com.wavesplatform.wallet.Wallet
import monix.eval.Task
import monix.reactive.Observable

trait Context {
  def settings: WavesSettings
  def blockchain: Blockchain
  def rollbackTo(blockId: ByteStr): Task[Either[ValidationError, DiscardedBlocks]]
  def time: Time
  def wallet: Wallet
  def utx: UtxPool

  def transactionsApi: CommonTransactionsApi
  def blocksApi: CommonBlocksApi
  def accountsApi: CommonAccountsApi
  def assetsApi: CommonAssetsApi

  def broadcastTransaction(tx: Transaction): TracedResult[ValidationError, Boolean]
  def spendableBalanceChanged: Observable[(Address, Asset)]
  def blockchainUpdated: Observable[BlockchainUpdated]
  def utxEvents: Observable[UtxEvent]
  def actorSystem: ActorSystem
} 
Example 196
Source File: MicroBlockMiner.scala    From Waves   with MIT License 5 votes vote down vote up
package com.wavesplatform.mining.microblocks

import com.wavesplatform.account.KeyPair
import com.wavesplatform.block.Block
import com.wavesplatform.mining.{MinerDebugInfo, MiningConstraint, MiningConstraints}
import com.wavesplatform.settings.MinerSettings
import com.wavesplatform.state.Blockchain
import com.wavesplatform.transaction.BlockchainUpdater
import com.wavesplatform.utx.UtxPool
import io.netty.channel.group.ChannelGroup
import monix.eval.Task
import monix.execution.schedulers.SchedulerService

trait MicroBlockMiner {
  def generateMicroBlockSequence(
      account: KeyPair,
      accumulatedBlock: Block,
      constraints: MiningConstraints,
      restTotalConstraint: MiningConstraint,
      lastMicroBlock: Long
  ): Task[Unit]
}

object MicroBlockMiner {
  def apply(
      setDebugState: MinerDebugInfo.State => Unit,
      allChannels: ChannelGroup,
      blockchainUpdater: BlockchainUpdater with Blockchain,
      utx: UtxPool,
      settings: MinerSettings,
      minerScheduler: SchedulerService,
      appenderScheduler: SchedulerService
  ): MicroBlockMiner =
    new MicroBlockMinerImpl(
      setDebugState,
      allChannels,
      blockchainUpdater,
      utx,
      settings,
      minerScheduler,
      appenderScheduler
    )
} 
Example 197
Source File: DebugApiRouteSpec.scala    From Waves   with MIT License 5 votes vote down vote up
package com.wavesplatform.http

import com.wavesplatform.api.http.ApiError.ApiKeyNotValid
import com.wavesplatform.network.PeerDatabase
import com.wavesplatform.settings.WavesSettings
import com.wavesplatform.{NTPTime, TestWallet}
import monix.eval.Task

//noinspection ScalaStyle
class DebugApiRouteSpec extends RouteSpec("/debug") with RestAPISettingsHelper with TestWallet with NTPTime {

  private val sampleConfig  = com.typesafe.config.ConfigFactory.load()
  private val wavesSettings = WavesSettings.fromRootConfig(sampleConfig)
  private val configObject  = sampleConfig.root()
  private val route =
    DebugApiRoute(
      wavesSettings,
      ntpTime,
      null,
      null,
      null,
      null,
      null,
      PeerDatabase.NoOp,
      null,
      (_, _) => Task.raiseError(new NotImplementedError("")),
      null,
      null,
      null,
      null,
      null,
      null,
      configObject,
      _ => Seq.empty
    ).route

  routePath("/configInfo") - {
    "requires api-key header" in {
      Get(routePath("/configInfo?full=true")) ~> route should produce(ApiKeyNotValid)
      Get(routePath("/configInfo?full=false")) ~> route should produce(ApiKeyNotValid)
    }
  }
} 
Example 198
Source File: MicroBlockMinerSpec.scala    From Waves   with MIT License 5 votes vote down vote up
package com.wavesplatform.mining

import com.wavesplatform.account.Alias
import com.wavesplatform.block.Block
import com.wavesplatform.common.utils._
import com.wavesplatform.db.WithDomain
import com.wavesplatform.features.BlockchainFeatures
import com.wavesplatform.lagonaki.mocks.TestBlock
import com.wavesplatform.mining.microblocks.MicroBlockMinerImpl
import com.wavesplatform.mining.microblocks.MicroBlockMinerImpl.MicroBlockMiningResult
import com.wavesplatform.settings.TestFunctionalitySettings
import com.wavesplatform.transaction.{CreateAliasTransaction, GenesisTransaction, TxVersion}
import com.wavesplatform.utils.Schedulers
import com.wavesplatform.utx.UtxPoolImpl
import com.wavesplatform.{TestValues, TransactionGen}
import monix.eval.Task
import monix.execution.Scheduler
import org.scalamock.scalatest.PathMockFactory
import org.scalatest.{FlatSpec, Matchers, PrivateMethodTester}

import scala.concurrent.duration._
import scala.util.Random

class MicroBlockMinerSpec extends FlatSpec with Matchers with PrivateMethodTester with PathMockFactory with WithDomain with TransactionGen {
  "Micro block miner" should "generate microblocks in flat interval" in {
    val scheduler = Schedulers.singleThread("test")
    val acc       = TestValues.keyPair
    val genesis   = GenesisTransaction.create(acc.toAddress, TestValues.bigMoney, TestValues.timestamp).explicitGet()
    val settings  = domainSettingsWithFS(TestFunctionalitySettings.withFeatures(BlockchainFeatures.NG))
    withDomain(settings) { d =>
      d.appendBlock(TestBlock.create(Seq(genesis)))
      val utxPool = new UtxPoolImpl(ntpTime, d.blockchainUpdater, ignoreSpendableBalanceChanged, settings.utxSettings, enablePriorityPool = true)
      val microBlockMiner = new MicroBlockMinerImpl(
        _ => (),
        null,
        d.blockchainUpdater,
        utxPool,
        settings.minerSettings,
        scheduler,
        scheduler
      )
      val generateOneMicroBlockTask = PrivateMethod[Task[MicroBlockMiningResult]](Symbol("generateOneMicroBlockTask"))

      def generateBlocks(
          block: Block,
          constraint: MiningConstraint,
          lastMicroBlock: Long
      ): Block = {
        val task = microBlockMiner invokePrivate generateOneMicroBlockTask(
          acc,
          block,
          MiningConstraints(d.blockchainUpdater, d.blockchainUpdater.height, Some(settings.minerSettings)),
          constraint,
          lastMicroBlock
        )
        import Scheduler.Implicits.global
        val startTime = System.nanoTime()
        val tx = CreateAliasTransaction
          .selfSigned(TxVersion.V1, acc, Alias.create("test" + Random.nextInt()).explicitGet(), TestValues.fee, TestValues.timestamp)
          .explicitGet()
        utxPool.putIfNew(tx).resultE.explicitGet()
        val result = task.runSyncUnsafe()
        result match {
          case res @ MicroBlockMinerImpl.Success(b, totalConstraint) =>
            val isFirstBlock = block.transactionData.isEmpty
            val elapsed = (res.nanoTime - startTime).nanos.toMillis

            if (isFirstBlock) elapsed should be < 1000L
            else elapsed shouldBe settings.minerSettings.microBlockInterval.toMillis +- 1000

            generateBlocks(b, totalConstraint, res.nanoTime)
          case MicroBlockMinerImpl.Stop =>
            d.blockchainUpdater.liquidBlock(d.blockchainUpdater.lastBlockId.get).get
          case MicroBlockMinerImpl.Retry =>
            throw new IllegalStateException()
        }
      }

      val baseBlock = Block
        .buildAndSign(
          3,
          TestValues.timestamp,
          d.lastBlockId,
          d.lastBlock.header.baseTarget,
          d.lastBlock.header.generationSignature,
          Nil,
          acc,
          Nil,
          0
        )
        .explicitGet()

      d.appendBlock(baseBlock)

      val constraint = OneDimensionalMiningConstraint(5, TxEstimators.one, "limit")
      val lastBlock = generateBlocks(baseBlock, constraint, 0)
      lastBlock.transactionData should have size constraint.rest.toInt
    }
    }
} 
Example 199
Source File: Backlog4jInterpreter.scala    From BacklogMigration-Redmine   with MIT License 5 votes vote down vote up
package com.nulabinc.backlog.r2b.interpreters.backlog

import cats.~>
import com.nulabinc.backlog.r2b.dsl.BacklogDSL.{BacklogProgram, BacklogResponse}
import com.nulabinc.backlog.r2b.dsl._
import com.nulabinc.backlog4j.api.option.GetIssuesParams
import com.nulabinc.backlog4j.conf.BacklogPackageConfigure
import com.nulabinc.backlog4j.{BacklogClient, BacklogClientFactory, Issue}
import monix.eval.Task

import scala.concurrent.ExecutionContext
import scala.jdk.CollectionConverters._

class Backlog4jInterpreter(url: String, key: String)
                          (implicit val exc: ExecutionContext) extends (BacklogADT ~> Task) {

  private val backlogPackageConfigure = new BacklogPackageConfigure(url)
  private val configure = backlogPackageConfigure.apiKey(key)
  private val client: BacklogClient = new BacklogClientFactory(configure).newClient()

  def run[A](program: BacklogProgram[A]): Task[A] =
    program.foldMap(this)

  def getProjectIssues(projectId: Long, offset: Int, count: Int): Seq[Issue] = {
    val params: GetIssuesParams = new GetIssuesParams(List(projectId).asJava)
    params.offset(offset.toLong)
    params.count(count)
    params.sort(GetIssuesParams.SortKey.Created)
    params.order(GetIssuesParams.Order.Asc)
    client.getIssues(params).asScala.toSeq
  }

  def deleteIssue(issue: Issue): Task[BacklogResponse[Unit]] = Task {
    try {
      Right(client.deleteIssue(issue.getId))
    } catch {
      case ex: Throwable => Left(ResponseError(ex))
    }
  }

  override def apply[A](fa: BacklogADT[A]): Task[A] = fa match {
    case Pure(a) => Task(a)
    case GetProject(projectKey) =>
      runRequest()(client.getProject(projectKey))
    case GetProjectIssues(projectId, offset, count) =>
      runRequest()(getProjectIssues(projectId, offset, count))
    case DeleteIssue(issue) => deleteIssue(issue)
  }

  private def runRequest[A]()(f: A): Task[BacklogResponse[A]] = Task {
    try {
      Right(f)
    } catch {
      case ex: Throwable => Left(ResponseError(ex))
    }
  }

} 
Example 200
Source File: AppInterpreter.scala    From BacklogMigration-Redmine   with MIT License 5 votes vote down vote up
package com.nulabinc.backlog.r2b.interpreters

import cats.free.Free
import cats.~>
import com.nulabinc.backlog.r2b.dsl.BacklogDSL.BacklogProgram
import com.nulabinc.backlog.r2b.interpreters.AppDSL.AppProgram
import com.nulabinc.backlog.r2b.interpreters.ConsoleDSL.ConsoleProgram
import com.nulabinc.backlog.r2b.interpreters.backlog.Backlog4jInterpreter
import monix.eval.Task

sealed trait AppADT[+A]
case class Pure[A](a: A) extends AppADT[A]
case class Backlog[A](prg: BacklogProgram[A]) extends AppADT[A]
case class Console[A](prg: ConsoleProgram[A]) extends AppADT[A]
case class Exit(exitCode: Int) extends AppADT[Unit]

object AppDSL {

  type AppProgram[A] = Free[AppADT, A]

  def pure[A](a: A): AppProgram[A] =
    Free.liftF(Pure(a))

  def backlog[A](prg: BacklogProgram[A]): AppProgram[A] =
    Free.liftF[AppADT, A](Backlog(prg))

  def console[A](prg: ConsoleProgram[A]): AppProgram[A] =
    Free.liftF(Console(prg))

  def exit(reason: String, exitCode: Int): AppProgram[Unit] = {
    for {
      _ <- console(ConsoleDSL.print(reason))
      _ <- Free.liftF(Exit(exitCode))
    } yield ()
  }

}

case class AppInterpreter(backlogInterpreter: Backlog4jInterpreter,
                          consoleInterpreter: ConsoleInterpreter) extends (AppADT ~> Task) {

  def run[A](prg: AppProgram[A]): Task[A] =
    prg.foldMap(this)

  override def apply[A](fa: AppADT[A]): Task[A] = fa match {
    case Pure(a) => Task(a)
    case Backlog(prg) => backlogInterpreter.run(prg)
    case Console(prg) => prg.foldMap(consoleInterpreter)
    case Exit(statusCode) => sys.exit(statusCode)
  }
}