cats.effect.Sync Scala Examples

The following examples show how to use cats.effect.Sync. 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: NatPmpClient.scala    From iotchain   with MIT License 6 votes vote down vote up
package jbok.network.nat

import cats.effect.Sync
import cats.effect.concurrent.Ref
import cats.implicits._
import com.offbynull.portmapper.gateways.network.NetworkGateway
import com.offbynull.portmapper.gateways.network.internalmessages.KillNetworkRequest
import com.offbynull.portmapper.gateways.process.ProcessGateway
import com.offbynull.portmapper.gateways.process.internalmessages.KillProcessRequest
import com.offbynull.portmapper.mapper.{MappedPort, PortType}
import com.offbynull.portmapper.mappers.natpmp.NatPmpPortMapper
import jbok.common.log.Logger

object NatPmpClient {
  def apply[F[_]](implicit F: Sync[F]): F[Nat[F]] =
    for {
      network <- F.delay(NetworkGateway.create)
      networkBus = network.getBus
      process <- F.delay(ProcessGateway.create)
      processBus = process.getBus
      mappers     <- F.delay(NatPmpPortMapper.identify(networkBus, processBus))
      mapper      <- F.delay(mappers.get(0))
      mappedPorts <- Ref.of[F, Map[Int, MappedPort]](Map.empty)
      _           <- F.delay(network.getBus.send(new KillNetworkRequest()))
      _           <- F.delay(process.getBus.send(new KillProcessRequest()))
    } yield
      new Nat[F] {
        private[this] val log = Logger[F]

        override def addMapping(internalPort: Int, externalPort: Int, lifetime: Long): F[Unit] =
          for {
            _ <- deleteMapping(externalPort)
            port <- F
              .delay(mapper.mapPort(PortType.TCP, internalPort, externalPort, lifetime))
              .handleErrorWith { e =>
                log.error(s"add port mapping from ${internalPort} to ${externalPort} failed", e) >>
                F.raiseError(e)
              }
            _ <- mappedPorts.update(_ + (externalPort -> port))
          } yield ()

        override def deleteMapping(externalPort: Int): F[Unit] =
          for {
            portOpt <- mappedPorts.get.map(_.get(externalPort))
            _       <- portOpt.fold(F.unit)(port => F.delay(mapper.unmapPort(port)))
            _       <- mappedPorts.update(_ - externalPort)
          } yield ()
      }
} 
Example 2
Source File: Http4sRpcServer.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.network.rpc.http

import cats.effect.{ConcurrentEffect, Resource, Sync, Timer}
import cats.implicits._
import io.circe.Json
import io.circe.syntax._
import jbok.network.rpc.{RpcRequest, RpcService}
import org.http4s.HttpRoutes
import org.http4s.circe.CirceEntityCodec._
import org.http4s.dsl.Http4sDsl
import org.http4s.implicits._
import org.http4s.server.Server
import org.http4s.server.blaze.BlazeServerBuilder

object Http4sRpcServer {
  def routes[F[_]](service: RpcService[F, Json])(implicit F: Sync[F]): HttpRoutes[F] = {
    val dsl = Http4sDsl[F]
    import dsl._

    HttpRoutes.of[F] {
      case req @ POST -> path =>
        for {
          json   <- req.as[Json]
          result <- service.handle(RpcRequest(path.toList, json))
          resp   <- Ok(result.asJson)
        } yield resp
    }
  }

  def server[F[_]](service: RpcService[F, Json])(implicit F: ConcurrentEffect[F], T: Timer[F]): Resource[F, Server[F]] =
    BlazeServerBuilder[F]
      .bindLocal(0)
      .withHttpApp(routes[F](service).orNotFound)
      .withWebSockets(true)
      .resource
} 
Example 3
Source File: implicits.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.network.tcp

import cats.effect.{Concurrent, ContextShift, IO, Sync}
import cats.implicits._
import fs2.Chunk
import fs2.io.tcp.Socket
import javax.net.ssl.SSLContext
import jbok.common.thread.ThreadUtil
import jbok.crypto.ssl.SSLContextHelper
import jbok.network.Message
import spinoco.fs2.crypto.io.tcp.TLSSocket

import scala.concurrent.ExecutionContext
import scala.concurrent.duration._

object implicits {
  val maxBytes: Int           = 4 * 1024 * 1024
  val timeout                 = Some(10.seconds)
  val sslEC: ExecutionContext = ThreadUtil.blockingThreadPool[IO]("jbok-tls").allocated.unsafeRunSync()._1

  implicit class TcpSocketOps[F[_]](val socket: Socket[F]) extends AnyVal {
    def readMessage(implicit F: Sync[F]): F[Message[F]] =
      socket.read(maxBytes, timeout).flatMap {
        case Some(chunk) => Message.decodeChunk(chunk)
        case None        => F.raiseError(new Exception(s"socket already closed"))
      }

    def writeMessage(message: Message[F]): F[Unit] =
      socket.write(Chunk.array(Message.encodeBytes(message).byteArray), timeout)

    def toTLSSocket(sslOpt: Option[SSLContext], client: Boolean)(implicit F: Concurrent[F], cs: ContextShift[F]): F[Socket[F]] =
      sslOpt match {
        case Some(ssl) =>
          if (client) TLSSocket.instance(socket, SSLContextHelper.clientEngine(ssl).engine, sslEC).widen[Socket[F]]
          else TLSSocket.instance(socket, SSLContextHelper.serverEngine(ssl).engine, sslEC).widen[Socket[F]]
        case None => F.pure(socket)
      }
  }
} 
Example 4
Source File: MetricsMiddleware.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.network.http.server.middleware

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

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

  def apply[F[_]](routes: HttpRoutes[F], enableMetrics: Boolean)(implicit F: Effect[F], clock: Clock[F]): F[HttpRoutes[F]] =
    if (enableMetrics) {
      Prometheus[F](PrometheusMetrics.registry, "iotchain_http_server").map { metricsOps =>
        middleware.Metrics[F](metricsOps)(routes)
      }
    } else {
      F.pure(routes)
    }
} 
Example 5
Source File: HmacAuthMiddleware.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.network.http.server.middleware

import java.time.{Duration, Instant}

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

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

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

object HmacAuthMiddleware {
  val defaultDuration: FiniteDuration = 5.minutes

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

  def apply[F[_]: Sync](key: MacSigningKey[HMACSHA256], duration: FiniteDuration = defaultDuration)(routes: HttpRoutes[F]): HttpRoutes[F] =
    Kleisli { req: Request[F] =>
      verifyFromHeader(req, key, duration) match {
        case Left(error) => OptionT.some[F](Response[F](Status.Forbidden).withEntity(error.message))
        case Right(_)    => routes(req)
      }
    }
} 
Example 6
Source File: Nat.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.network.nat

import cats.effect.Sync

sealed trait NatType
case object NatPMP  extends NatType
case object NatUPnP extends NatType

trait Nat[F[_]] {
  def addMapping(internalPort: Int, externalPort: Int, lifetime: Long): F[Unit]

  def deleteMapping(externalPort: Int): F[Unit]
}

object Nat {
  def apply[F[_]: Sync](natType: NatType): F[Nat[F]] =
    natType match {
      case NatPMP  => NatPmpClient[F]
      case NatUPnP => NatUpnpClient[F]
    }
} 
Example 7
Source File: NatUpnpClient.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.network.nat

import cats.effect.Sync
import cats.implicits._
import jbok.common.log.Logger
import org.bitlet.weupnp.GatewayDiscover

object NatUpnpClient {
  def apply[F[_]](implicit F: Sync[F]): F[Nat[F]] =
    for {
      discover <- F.delay(new GatewayDiscover)
      _      = discover.discover()
      device = discover.getValidGateway
    } yield
      new Nat[F] {
        private[this] val log = Logger[F]

        override def addMapping(internalPort: Int, externalPort: Int, lifetime: Long): F[Unit] =
          for {
            _      <- deleteMapping(externalPort)
            result <- F.delay(device.addPortMapping(externalPort, internalPort, device.getLocalAddress.getHostAddress, "TCP", "jbok"))
            _      <- log.debug(s"add port mapping result ${result}")
          } yield ()

        override def deleteMapping(externalPort: Int): F[Unit] =
          F.delay(device.deletePortMapping(externalPort, "TCP")).void
      }
} 
Example 8
Source File: RpcService.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.network.rpc

import cats.effect.Sync
import jbok.network.rpc.internal.RpcServiceMacro

import scala.language.experimental.macros

final class RpcService[F[_], P](val apiMap: RpcService.Map[F, P])(implicit F: Sync[F]) {
  def handle(request: RpcRequest[P]): F[RpcResponse[P]] = {
    def notFoundFailure(path: List[String]): F[RpcResponse[P]] =
      F.pure(RpcResponse.Failure[P](404, s"Path ${path.mkString("/")} not found"))

    request.path match {
      case apiName :: methodName :: Nil =>
        val function: Option[P => F[RpcResponse[P]]] = apiMap.get(apiName).flatMap(_.get(methodName))
        function.fold(notFoundFailure(apiName :: methodName :: Nil))(f => f(request.payload))

      case _ => notFoundFailure(request.path)
    }
  }

  def mount[API](impl: API)(implicit F: Sync[F]): RpcService[F, P] =
    macro RpcServiceMacro.impl[API, F, P]

  def orElse(name: String, value: RpcService.MapValue[F, P]): RpcService[F, P] =
    new RpcService(apiMap + (name -> value))
}

object RpcService {
  type MapValue[F[_], P] = collection.Map[String, P => F[RpcResponse[P]]]

  type Map[F[_], P] = collection.Map[String, MapValue[F, P]]

  def apply[F[_], P](implicit F: Sync[F]): RpcService[F, P] = new RpcService[F, P](collection.mutable.HashMap.empty)
} 
Example 9
Source File: RpcClientImpl.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.network.rpc.internal

import cats.effect.Sync
import cats.implicits._
import jbok.codec._
import jbok.network.rpc.{RpcClient, RpcRequest, RpcResponse}

final class RpcClientImpl[F[_], P](client: RpcClient[F, P])(implicit F: Sync[F]) {
  def execute[A <: Product, B](path: List[String], arguments: A)(implicit encoder: Encoder[A, P], decoder: Decoder[B, P]): F[B] = {
    val encoded = encoder.encode(arguments)
    val request = RpcRequest(path, encoded)
    client.transport.fetch(request).flatMap {
      case RpcResponse.Success(payload) =>
        decoder.decode(payload) match {
          case Left(e)      => F.raiseError[B](e)
          case Right(value) => F.pure[B](value)
        }

      case RpcResponse.Failure(code, reason, data) =>
        F.raiseError[B](new Exception(s"client failure: code=${code},reason=${reason},data=${data}"))
    }
  }
} 
Example 10
Source File: RpcServiceImpl.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.network.rpc.internal

import cats.effect.Sync
import cats.implicits._
import jbok.codec._
import jbok.common.log.Logger
import jbok.network.rpc.RpcResponse

final class RpcServiceImpl[F[_], P](implicit F: Sync[F]) {
  private[this] val log = Logger[F]

  def execute[A <: Product, B](payload: P)(call: A => F[B])(implicit decoder: Decoder[A, P], encoder: Encoder[B, P]): F[RpcResponse[P]] =
    decoder.decode(payload) match {
      case Left(err) =>
        log.info(s"decode error:${err}") >>
          F.pure(RpcResponse.Failure[P](400, s"decode error"))

      case Right(arguments) =>
        call(arguments).map { result =>
          RpcResponse.Success(encoder.encode(result))
        }
    }
} 
Example 11
Source File: StoreUpdateService.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.app.service

import cats.data.OptionT
import cats.effect.{Sync, Timer}
import cats.implicits._
import fs2._
import jbok.app.service.store.{BlockStore, TransactionStore}
import jbok.common.log.Logger
import jbok.common.math.N
import jbok.core.ledger.History
import spire.compat._

import scala.concurrent.duration._

final class StoreUpdateService[F[_]](history: History[F], blockStore: BlockStore[F], txStore: TransactionStore[F])(implicit F: Sync[F], T: Timer[F]) {
  private[this] val log = Logger[F]

  def findForkPoint(start: N): F[N] =
    for {
      hash1 <- blockStore.getBlockHashByNumber(start)
      hash2 <- history.getHashByBlockNumber(start)
      number <- (hash1, hash2) match {
        case (Some(h1), Some(h2)) if h1 == h2 => F.pure(start)
        case (Some(_), Some(_))               => findForkPoint(start - 1)
        case _                                => F.raiseError(new Exception(s"fatal error"))
      }
    } yield number

  private def delRange(start: N, end: N): F[Unit] =
    List.range(start, end + 1).traverse_ { number =>
      blockStore.delByBlockNumber(number) >> txStore.delByBlockNumber(number)
    }

  private def syncRange(start: N, end: N): F[Unit] =
    List.range(start, end + 1).traverse_ { number =>
      syncBlock(number) >> syncTransactions(number)
    }

  private def syncBlock(number: N): F[Unit] =
    for {
      header <- history.getBlockHeaderByNumber(number)
      _      <- header.fold(F.unit)(header => blockStore.insert(header.number, header.hash))
    } yield ()

  private def syncTransactions(number: N): F[Unit] =
    (for {
      hash     <- OptionT(history.getHashByBlockNumber(number))
      block    <- OptionT(history.getBlockByHash(hash))
      receipts <- OptionT(history.getReceiptsByHash(hash))
      _        <- OptionT.liftF(txStore.insertBlockTransactions(block, receipts))
    } yield ()).value.void

  def sync: F[Unit] =
    for {
      currentOpt <- blockStore.getBestBlockNumber
      fork       <- currentOpt.fold(N(0).pure[F])(current => findForkPoint(current))
      best       <- history.getBestBlockNumber
      _          <- log.i(s"current: ${fork}, best: ${best}")
      _ <- if (fork == best) {
        F.unit
      } else {
        delRange(fork, best) >> syncRange(fork, best)
      }
    } yield ()

  val stream: Stream[F, Unit] =
    Stream.eval(log.i(s"starting App/StoreUpdateService")) ++
      Stream.repeatEval(sync).metered(10.seconds)
} 
Example 12
Source File: PersonalService.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.app.service

import cats.effect.Sync
import cats.implicits._
import jbok.common.math.N
import jbok.core.api.PersonalAPI
import jbok.core.config.HistoryConfig
import jbok.core.keystore.KeyStore
import jbok.core.ledger.History
import jbok.core.models.{Address, Transaction}
import jbok.core.pool.TxPool
import scodec.bits.ByteVector

import scala.util.Try

final class PersonalService[F[_]](
    historyConfig: HistoryConfig,
    keyStore: KeyStore[F],
    history: History[F],
    txPool: TxPool[F]
)(implicit F: Sync[F])
    extends PersonalAPI[F] {

  override def importRawKey(privateKey: ByteVector, passphrase: String): F[Address] =
    keyStore.importPrivateKey(privateKey, passphrase)

  override def newAccount(passphrase: String): F[Address] =
    keyStore.newAccount(passphrase)

  override def delAccount(address: Address): F[Boolean] =
    keyStore.deleteAccount(address)

  override def listAccounts: F[List[Address]] =
    keyStore.listAccounts

  override def sendTransaction(
      from: Address,
      passphrase: String,
      to: Option[Address],
      value: Option[N],
      gasLimit: Option[N],
      gasPrice: Option[N],
      nonce: Option[N],
      data: Option[ByteVector]
  ): F[ByteVector] = {

    val defaultGasPrice: N = 2 * N(10).pow(10)
    val defaultGasLimit: N = N(90000)

    for {
      wallet  <- keyStore.unlockAccount(from, passphrase)
      pending <- txPool.getPendingTransactions
      latestNonceOpt = Try(pending.collect {
        case (stx, _) if stx.senderAddress.contains(wallet.address) => stx.nonce
      }.max).toOption
      bn              <- history.getBestBlockNumber
      currentNonceOpt <- history.getAccount(from, bn).map(_.map(_.nonce.toN))
      maybeNextTxNonce = latestNonceOpt.map(_ + 1).orElse(currentNonceOpt)
      tx = Transaction(
        nonce = nonce.getOrElse(maybeNextTxNonce.getOrElse(historyConfig.accountStartNonce)),
        gasPrice = gasPrice.getOrElse(defaultGasPrice),
        gasLimit = gasLimit.getOrElse(defaultGasLimit),
        receivingAddress = to,
        value = value.getOrElse(N(0)),
        payload = data.getOrElse(ByteVector.empty)
      )
      stx <- wallet.signTx[F](tx, history.chainId)
      _   <- txPool.addOrUpdateTransaction(stx)
    } yield stx.hash
  }

  override def changePassphrase(address: Address, oldPassphrase: String, newPassphrase: String): F[Boolean] =
    keyStore.changePassphrase(address, oldPassphrase, newPassphrase)
} 
Example 13
Source File: AdminService.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.app.service

import cats.effect.Sync
import cats.implicits._
import jbok.app.service.store.{BlockStore, TransactionStore}
import jbok.common.log.Logger
import jbok.common.math.N
import jbok.core.CoreNode
import jbok.core.config.FullConfig
import jbok.core.models.SignedTransaction
import jbok.core.peer.PeerUri
import jbok.core.api.AdminAPI

final class AdminService[F[_]](
    core: CoreNode[F],
    blockStore: BlockStore[F],
    txStore: TransactionStore[F]
)(implicit F: Sync[F]) extends AdminAPI[F] {
  private[this] val log = Logger[F]

  override def peerUri: F[String] =
    core.peerManager.incoming.localPeerUri.map(_.toString)

  override def addPeer(peerUri: String): F[Unit] =
    for {
      peerUri <- F.fromEither(PeerUri.fromStr(peerUri))
      _       <- log.info(s"add a peer: ${peerUri}")
      _       <- core.peerManager.outgoing.store.add(peerUri)
    } yield ()

  override def dropPeer(peerUri: String): F[Unit] =
    for {
      peerUri <- F.fromEither(PeerUri.fromStr(peerUri))
      _       <- log.info(s"drop a peer: ${peerUri}")
      _       <- core.peerManager.close(peerUri)
    } yield ()

  override def incomingPeers: F[List[PeerUri]] =
    core.peerManager.incoming.connected.get.map(_.values.map(_._1.uri).toList)

  override def outgoingPeers: F[List[PeerUri]] =
    core.peerManager.outgoing.connected.get.map(_.values.map(_._1.uri).toList)

  override def pendingTransactions: F[List[SignedTransaction]] =
    core.txPool.getPendingTransactions.map(_.keys.toList)

  override def getConfig: F[FullConfig] =
    F.pure(core.config)

  override def deleteBlockUntil(number: N): F[Unit] = {
    def delBlock(n: N, from: N): F[Unit] = {
      if (from > n) core.history.getBlockHeaderByNumber(from).flatMap{
        case Some(block) => for {
          _ <- core.history.delBlock(block.hash)
          _ <- delBlock(n, from-1)
        }yield ()
        case _ => F.unit
      }
      else F.unit
    }

    for {
      best   <- core.history.getBestBlock
      bestNumber = best.header.number
      _ <- List.range((number+1).toBigInt, (bestNumber + 1).toBigInt).traverse_ { n =>
        blockStore.delByBlockNumber(n) >> txStore.delByBlockNumber(n)}
      _ <- delBlock(number, bestNumber)
      _ <- core.history.putBestBlockNumber(number)
    }yield ()

  }
} 
Example 14
Source File: AccountService.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.app.service

import cats.effect.Sync
import cats.implicits._
import jbok.app.service.store.TransactionStore
import jbok.common.math.N
import jbok.core.config.HistoryConfig
import jbok.core.ledger.History
import jbok.core.models.{Account, Address, SignedTransaction}
import jbok.core.pool.TxPool
import jbok.core.api.{AccountAPI, BlockTag, HistoryTransaction}
import scodec.bits.ByteVector

final class AccountService[F[_]](config: HistoryConfig, history: History[F], txPool: TxPool[F], helper: ServiceHelper[F], txStore: TransactionStore[F])(implicit F: Sync[F])
    extends AccountAPI[F] {

  override def getAccount(address: Address, tag: BlockTag): F[Account] =
    for {
      account <- helper.resolveAccount(address, tag)
    } yield account

  override def getCode(address: Address, tag: BlockTag): F[ByteVector] =
    for {
      block <- helper.resolveBlock(tag)
      world <- history.getWorldState(config.accountStartNonce, block.map(_.header.stateRoot))
      code  <- world.getCode(address)
    } yield code

  override def getBalance(address: Address, tag: BlockTag): F[N] =
    for {
      account <- helper.resolveAccount(address, tag)
    } yield account.balance.toN

  override def getStorageAt(address: Address, position: N, tag: BlockTag): F[ByteVector] =
    for {
      account <- helper.resolveAccount(address, tag)
      storage <- history.getStorage(account.storageRoot, position)
    } yield storage

  override def getTransactions(address: Address, page: Int, size: Int): F[List[HistoryTransaction]] = {
    val validSize = if (size < 0) 100 else 1000000.min(size)
    txStore.findTransactionsByAddress(address.toString, page.max(1), validSize)
  }

  override def getTransactionsByNumber(number: Int): F[List[HistoryTransaction]] =
    txStore.findTransactionsByNumber(number)

  override def getPendingTxs(address: Address): F[List[SignedTransaction]] =
    txPool.getPendingTransactions.map(_.keys.toList.filter(_.senderAddress.exists(_ == address)))

  override def getEstimatedNonce(address: Address): F[N] =
    for {
      pending <- txPool.getPendingTransactions
      latestNonceOpt = scala.util
        .Try(pending.collect {
          case (stx, _) if stx.senderAddress contains address => stx.nonce
        }.max)
        .toOption
      bn              <- history.getBestBlockNumber
      currentNonceOpt <- history.getAccount(address, bn).map(_.map(_.nonce.toN))
      defaultNonce     = config.accountStartNonce.toN
      maybeNextTxNonce = latestNonceOpt.map(_ + 1).getOrElse(defaultNonce) max currentNonceOpt.getOrElse(defaultNonce)
    } yield maybeNextTxNonce
} 
Example 15
Source File: ServiceHelper.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.app.service

import cats.effect.Sync
import cats.implicits._
import jbok.common.math.N
import jbok.common.math.implicits._
import jbok.core.config.HistoryConfig
import jbok.core.ledger.History
import jbok.core.models.{Account, Address, Block}
import jbok.core.api.BlockTag

final class ServiceHelper[F[_]](config: HistoryConfig, history: History[F])(implicit F: Sync[F]) {
  def resolveBlock(tag: BlockTag): F[Option[Block]] = {
    def getBlock(number: N): F[Option[Block]] =
      history.getBlockByNumber(number)

    tag match {
      case BlockTag.Number(number) if number >= 0 => getBlock(number)
      case BlockTag.latest                        => history.getBestBlockNumber >>= getBlock
      case _                                      => F.pure(None)
    }
  }

  def resolveAccount(address: Address, tag: BlockTag): F[Account] =
    for {
      blockOpt <- resolveBlock(tag)
      account <- blockOpt match {
        case Some(block) =>
          history
            .getAccount(address, block.header.number)
            .map(_.getOrElse(Account.empty(config.accountStartNonce)))
        case None =>
          F.pure(Account.empty(config.accountStartNonce))
      }
    } yield account

} 
Example 16
Source File: ContractService.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.app.service

import cats.effect.Sync
import cats.implicits._
import jbok.common.math.N
import jbok.core.ledger.{BlockExecutor, History}
import jbok.core.models.{Address, Block, SignedTransaction, Transaction}
import jbok.core.api.{BlockTag, CallTx, ContractAPI}
import scodec.bits.ByteVector
import spire.syntax.all._
import spire.compat._

final class ContractService[F[_]](history: History[F], executor: BlockExecutor[F], helper: ServiceHelper[F])(implicit F: Sync[F]) extends ContractAPI[F] {
//  override def getABI(address: Address): F[Option[Ast.ContractDef]] = ???
//
//  override def getSourceCode(address: Address): F[Option[String]] = ???

  override def call(callTx: CallTx, tag: BlockTag): F[ByteVector] =
    for {
      (stx, block) <- doCall(callTx, tag)
      txResult     <- executor.simulateTransaction(stx, callTx.from.getOrElse(Address.empty), block.header)
    } yield txResult.vmReturnData

  override def getEstimatedGas(callTx: CallTx, tag: BlockTag): F[N] =
    for {
      (stx, block) <- doCall(callTx, tag)
      gas          <- executor.binarySearchGasEstimation(stx, callTx.from.getOrElse(Address.empty), block.header)
    } yield gas

  override def getGasPrice: F[N] = {
    val blockDifference = N(30)
    for {
      bestBlock <- history.getBestBlockNumber
      gasPrices <- List
        .range(bestBlock - blockDifference, bestBlock)
        .filter(_ >= N(0))
        .traverse(history.getBlockByNumber)
        .map(_.flatten.flatMap(_.body.transactionList).map(_.gasPrice))
      gasPrice = if (gasPrices.nonEmpty) {
        gasPrices.qsum / gasPrices.length
      } else {
        N(0)
      }
    } yield gasPrice
  }

  private def doCall[A](callTx: CallTx, tag: BlockTag): F[(SignedTransaction, Block)] =
    for {
      stx      <- prepareTransaction(callTx, tag)
      blockOpt <- helper.resolveBlock(tag)
      block    <- F.fromOption(blockOpt, new Exception())
    } yield (stx, block)

  private def prepareTransaction(callTx: CallTx, tag: BlockTag): F[SignedTransaction] =
    for {
      gasLimit <- getGasLimit(callTx, tag)
      tx = Transaction(0, callTx.gasPrice, gasLimit, callTx.to, callTx.value, callTx.data)
    } yield SignedTransaction(tx, 0.toByte, ByteVector(0), ByteVector(0))

  private def getGasLimit(callTx: CallTx, tag: BlockTag): F[N] =
    callTx.gas match {
      case Some(gas) => gas.pure[F]
      case None      => helper.resolveBlock(tag).map(_.map(_.header.gasLimit).getOrElse(N(0)))
    }
} 
Example 17
Source File: TransactionService.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.app.service

import cats.data.OptionT
import cats.effect.Sync
import cats.implicits._
import jbok.codec.rlp.RlpEncoded
import jbok.core.ledger.History
import jbok.core.models.{Receipt, SignedTransaction}
import jbok.core.pool.TxPool
import jbok.core.api.{BlockTag, TransactionAPI}
import scodec.bits.ByteVector

final class TransactionService[F[_]](history: History[F], txPool: TxPool[F], helper: ServiceHelper[F])(implicit F: Sync[F]) extends TransactionAPI[F] {
  override def getTx(hash: ByteVector): F[Option[SignedTransaction]] =
    (for {
      loc   <- OptionT(history.getTransactionLocation(hash))
      block <- OptionT(history.getBlockByHash(loc.blockHash))
      stx   <- OptionT.fromOption[F](block.body.transactionList.lift(loc.txIndex))
    } yield stx).value

  override def getPendingTx(hash: ByteVector): F[Option[SignedTransaction]] =
    txPool.getPendingTransactions.map(_.keys.toList.find(_.hash == hash))

  override def getReceipt(hash: ByteVector): F[Option[Receipt]] =
    (for {
      loc      <- OptionT(history.getTransactionLocation(hash))
      block    <- OptionT(history.getBlockByHash(loc.blockHash))
      _        <- OptionT.fromOption[F](block.body.transactionList.lift(loc.txIndex))
      receipts <- OptionT(history.getReceiptsByHash(loc.blockHash))
      receipt  <- OptionT.fromOption[F](receipts.lift(loc.txIndex))
    } yield receipt).value

  override def getTxByBlockHashAndIndex(hash: ByteVector, index: Int): F[Option[SignedTransaction]] =
    (for {
      block <- OptionT(history.getBlockByHash(hash))
      stx   <- OptionT.fromOption[F](block.body.transactionList.lift(index))
    } yield stx).value

  override def getTxByBlockTagAndIndex(tag: BlockTag, index: Int): F[Option[SignedTransaction]] =
    (for {
      block <- OptionT(helper.resolveBlock(tag))
      stx   <- OptionT.fromOption[F](block.body.transactionList.lift(index))
    } yield stx).value

  override def sendTx(stx: SignedTransaction): F[ByteVector] =
    txPool.addOrUpdateTransaction(stx).as(stx.hash)

  override def sendRawTx(data: RlpEncoded): F[ByteVector] =
    for {
      stx <- F.fromEither(data.decoded[SignedTransaction])
      _   <- txPool.addOrUpdateTransaction(stx)
    } yield stx.hash
} 
Example 18
Source File: DoobieTransactionStore.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.app.service.store.doobie

import cats.effect.Sync
import cats.implicits._
import doobie.implicits._
import doobie.util.transactor.Transactor
import doobie.util.update.Update
import jbok.app.service.store.TransactionStore
import jbok.common.math.N
import jbok.core.api.HistoryTransaction
import jbok.core.models.{Receipt, Block => CoreBlock}

final class DoobieTransactionStore[F[_]](xa: Transactor[F])(implicit F: Sync[F]) extends TransactionStore[F] with DoobieSupport {
  def findTransactionsByAddress(address: String, page: Int, size: Int): F[List[HistoryTransaction]] =
    sql"""
       SELECT txHash, nonce, fromAddress, toAddress, value, payload, v, r, s, gasUsed, gasPrice, blockNumber, blockHash, location
       FROM transactions
       WHERE (fromAddress = ${address.toString} OR toAddress = ${address.toString})
       ORDER BY blockNumber, location DESC
       limit ${size} offset ${(page - 1) * size}
      """
      .query[HistoryTransaction]
      .to[List]
      .transact(xa)

  def findTransactionByHash(txHash: String): F[Option[HistoryTransaction]] =
    sql"""
       SELECT txHash, nonce, fromAddress, toAddress, value, payload, v, r, s, gasUsed, gasPrice, blockNumber, blockHash, location
       FROM transactions
       WHERE txHash = ${txHash}
       """
      .query[HistoryTransaction]
      .option
      .transact(xa)

  override def findTransactionsByNumber(blockNumber: Int): F[List[HistoryTransaction]] =
    sql"""
       SELECT txHash, nonce, fromAddress, toAddress, value, payload, v, r, s, gasUsed, gasPrice, blockNumber, blockHash, location
       FROM transactions
       WHERE (blockNumber = ${blockNumber})
       ORDER BY blockNumber, location DESC
      """
      .query[HistoryTransaction]
      .to[List]
      .transact(xa)

  override def delByBlockNumber(number: N): F[Unit] =
    sql"""DELETE from transactions WHERE blockNumber = $number""".update.run.void.transact(xa)

  def insertBlockTransactions(block: CoreBlock, receipts: List[Receipt]): F[Unit] = {
    require(block.body.transactionList.length == receipts.length)
    val xs = block.body.transactionList.zip(receipts).zipWithIndex.map {
      case ((stx, receipt), idx) =>
        (
          stx.hash.toHex,
          stx.nonce.toInt,
          stx.senderAddress.map(_.toString).getOrElse(""),
          stx.receivingAddress.toString,
          stx.value.toString,
          stx.payload.toHex,
          stx.v.toString,
          stx.r.toString,
          stx.s.toString,
          receipt.gasUsed.toString,
          stx.gasPrice.toString,
          block.header.number.toLong,
          block.header.hash.toHex,
          idx
        )
    }
    val holes = List.fill(14)("?").mkString(",")
    val sql =
      s"insert into transactions (txHash, nonce, fromAddress, toAddress, value, payload, v, r, s, gasUsed, gasPrice, blockNumber, blockHash, location) values ($holes)"
    Update[(String, Int, String, String, String, String, String, String, String, String, String, Long, String, Int)](sql)
      .updateMany(xs)
      .transact(xa)
      .void
  }
} 
Example 19
Source File: DoobieBlockStore.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.app.service.store.doobie

import cats.effect.Sync
import cats.implicits._
import doobie.implicits._
import doobie.util.transactor.Transactor
import jbok.app.service.store.BlockStore
import jbok.common.math.N
import scodec.bits.ByteVector

final class DoobieBlockStore[F[_]](xa: Transactor[F])(implicit F: Sync[F]) extends BlockStore[F] with DoobieSupport {
  override def getBestBlockNumber: F[Option[N]] =
    sql"""
      SELECT blockNumber
      FROM blocks
      ORDER BY blockNumber DESC
      LIMIT 1
      """
      .query[N]
      .option
      .transact(xa)

  override def getBestBlockNumberAndHash: F[(N, ByteVector)] =
    sql"""
      SELECT blockNumber, blockHash
      FROM blocks
      ORDER BY blockNumber DESC
      LIMIT 1
      """
      .query[(N, ByteVector)]
      .unique
      .transact(xa)

  override def getBlockHashByNumber(number: N): F[Option[ByteVector]] =
    sql"""
      SELECT blockHash
      FROM blocks
      WHERE blockNumber = $number
      """
      .query[ByteVector]
      .option
      .transact(xa)

  override def delByBlockNumber(number: N): F[Unit] =
    sql"""
      DELETE FROM blocks where blockNumber = $number
      """.update.run
      .transact(xa)
      .void

  override def insert(number: N, hash: ByteVector): F[Unit] =
    sql"""
      INSERT INTO blocks (blockNumber, blockHash) VALUES ($number, $hash)
      """.update.run.void.transact(xa)
} 
Example 20
Source File: Migration.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.app.service.store

import cats.effect.Sync
import cats.implicits._
import jbok.core.config.DatabaseConfig
import org.flywaydb.core.Flyway

object Migration {
  private val flyway = new Flyway()
  flyway.setLocations("db/mysql", "db/sqlite")

  def migrate[F[_]: Sync](config: DatabaseConfig): F[Unit] =
    Sync[F].delay {
      config.driver match {
        case "org.sqlite.JDBC" => flyway.setLocations("db/sqlite")
        case _                 => flyway.setLocations("db/mysql")
      }
      flyway.setDataSource(config.url, config.user, config.password)
      flyway.migrate()
    }.void
} 
Example 21
Source File: SSLContextHelper.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.crypto.ssl

import java.nio.file.Paths
import java.security.KeyStore

import cats.effect.Sync
import cats.implicits._
import javax.net.ssl.{KeyManagerFactory, SSLContext, SSLEngine, TrustManagerFactory}
import jbok.common.FileUtil
import jbok.common.log.Logger

final class ClientSSLEngine(val engine: SSLEngine) extends AnyVal

final class ServerSSLEngine(val engine: SSLEngine) extends AnyVal

object SSLContextHelper {
  def apply[F[_]](config: SSLConfig)(implicit F: Sync[F]): F[Option[SSLContext]] =
    if (!config.enabled) {
      F.pure(None)
    } else {
      Logger[F].i(s"init SSLContext from keyStore=${config.keyStorePath} trustStore=${config.trustStorePath}") >>
        FileUtil[F]
          .inputStream(Paths.get(config.keyStorePath))
          .use { keyStoreIS =>
            FileUtil[F].inputStream(Paths.get(config.trustStorePath)).use { trustStoreIS =>
              F.delay {
                val keyStore = KeyStore.getInstance("JKS")
                keyStore.load(keyStoreIS, "changeit".toCharArray)
                val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
                keyManagerFactory.init(keyStore, "changeit".toCharArray)

                val trustStore = KeyStore.getInstance("JKS")
                trustStore.load(trustStoreIS, "changeit".toCharArray)
                val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
                trustManagerFactory.init(trustStore)

                val ctx = SSLContext.getInstance(config.protocol)
                ctx.init(keyManagerFactory.getKeyManagers, trustManagerFactory.getTrustManagers, null)
                ctx
              }
            }
          }
          .map(_.some)
    }

  def clientEngine(ctx: SSLContext): ClientSSLEngine = {
    val engine = ctx.createSSLEngine()
    engine.setUseClientMode(true)
    engine.setNeedClientAuth(true)
    new ClientSSLEngine(engine)
  }

  def serverEngine(ctx: SSLContext): ServerSSLEngine = {
    val engine = ctx.createSSLEngine()
    engine.setUseClientMode(false)
    engine.setNeedClientAuth(true)
    new ServerSSLEngine(engine)
  }
} 
Example 22
Source File: SignaturePlatform.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.crypto.signature

import java.math.BigInteger
import java.util.Random

import cats.effect.Sync
import jbok.crypto.facade.{BN, EC, SignatureEC}

import scala.scalajs.js.JSConverters._
import scala.scalajs.js.typedarray.Uint8Array

trait SignaturePlatform {
  val ecdsa: Signature[ECDSA] = ECDSAPlatform
}

private object ECDSAPlatform extends Signature[ECDSA] {
  import ECDSACommon._
  val secp256k1 = new EC("secp256k1")

  override def generateKeyPair[F[_]](random: Option[Random])(implicit F: Sync[F]): F[KeyPair] = F.delay {
    val keyPair = secp256k1.genKeyPair()
    val secret  = KeyPair.Secret(keyPair.getPrivate("hex"))
    // drop uncompressed indicator, make it 64-bytes
    val pubkey = KeyPair.Public(keyPair.getPublic(false, "hex").drop(2))
    KeyPair(pubkey, secret)
  }

  override def generatePublicKey[F[_]](secret: KeyPair.Secret)(implicit F: Sync[F]): F[KeyPair.Public] = F.delay {
    val keyPair = secp256k1.keyFromPrivate(secret.bytes.toHex, "hex")
    // drop uncompressed indicator, make it 64-bytes
    KeyPair.Public(keyPair.getPublic(false, "hex").drop(2))
  }

  override def sign[F[_]](hash: Array[Byte], keyPair: KeyPair, chainId: BigInt)(implicit F: Sync[F]): F[CryptoSignature] = F.delay {
    val kp  = secp256k1.keyFromPrivate(keyPair.secret.bytes.toHex, "hex")
    val sig = secp256k1.sign(new Uint8Array(hash.toJSArray), kp)
    val r   = new BigInteger(sig.r.toString)
    val s   = new BigInteger(sig.s.toString)
    val pointSign = calculatePointSign(r, toCanonicalS(s), keyPair, hash, chainId) match {
      case Some(recId) => recId
      case None        => throw new Exception("unexpected error")
    }
    val rid: BigInt = getRecoveryId(chainId, pointSign).getOrElse(pointSign)
    CryptoSignature(r, toCanonicalS(s), rid)
  }

  override def verify[F[_]](hash: Array[Byte], sig: CryptoSignature, public: KeyPair.Public, chainId: BigInt)(implicit F: Sync[F]): F[Boolean] = F.delay {
    getPointSign(chainId, sig.v).exists { bigInt =>
      val signatureEC = convert(sig.copy(v = bigInt))
      val key         = secp256k1.keyFromPublic(UNCOMPRESSED_INDICATOR_STRING + public.bytes.toHex, "hex")
      secp256k1.verify(new Uint8Array(hash.toJSArray), signatureEC, key)
    }
  }

  override def recoverPublic(hash: Array[Byte], sig: CryptoSignature, chainId: BigInt): Option[KeyPair.Public] =
    getPointSign(chainId, sig.v).map { bigInt =>
      val signatureEC = convert(sig.copy(v = bigInt))
      val msg         = new Uint8Array(hash.toJSArray)
      val recId       = secp256k1.getKeyRecoveryParam(msg, signatureEC)
      val point       = secp256k1.recoverPubKey(new Uint8Array(hash.toJSArray), signatureEC, recId)
      KeyPair.Public(point.encode("hex", false).drop(2))
    }

  private def convert(sig: CryptoSignature) = {
    val r = new BN(sig.r.toString(16), 16)
    val s = new BN(sig.s.toString(16), 16)
    SignatureEC(r, s, recoveryParam = (sig.v - NEGATIVE_POINT_SIGN).toInt)
  }

  private def calculatePointSign(r: BigInt, s: BigInt, keyPair: KeyPair, hash: Array[Byte], chainId: BigInt): Option[BigInt] =
    allowedPointSigns.find(
      v =>
        recoverPublic(hash, CryptoSignature(r, s, getRecoveryId(chainId, v).getOrElse(v)), chainId)
          .contains(keyPair.public))
} 
Example 23
Source File: Terminal.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.common

import cats.effect.Sync
import cats.implicits._

object Terminal {
  private val reader = new scala.tools.jline_embedded.console.ConsoleReader

  def putStr[F[_]: Sync](s: String): F[Unit] = Sync[F].delay {
    print(s)
    System.out.flush()
  }

  def putStrLn[F[_]: Sync](s: String): F[Unit] = Sync[F].delay {
    println(s)
  }

  def readLn[F[_]: Sync](prompt: String): F[String] = Sync[F].delay {
    reader.readLine(prompt)
  }

  def readPassword[F[_]: Sync](prompt: String): F[String] = Sync[F].delay {
    reader.readLine(prompt, Character.valueOf(0))
  }

  def choose[F[_]: Sync](prompt: String): F[Boolean] =
    readLn[F](s"${prompt} [Y/N]").map {
      case x if x.toLowerCase.startsWith("y") => true
      case _                                  => false
    }
} 
Example 24
Source File: LoggerPlatform.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.common.log

import java.nio.file.{Path, Paths}

import cats.effect.Sync
import cats.implicits._
import jbok.common.FileUtil
import scribe.handler.LogHandler
import scribe.writer.FileWriter
import scribe.writer.file.LogPath

import scala.concurrent.duration._

object LoggerPlatform {
  def initConfig[F[_]: Sync](config: LogConfig): F[Unit] = {
    val level = Level.fromName(config.level)
    Logger.setRootLevel(level) >>
      (config.logDir match {
        case "/dev/null" =>
          Logger.setRootHandlers(Logger.consoleHandler(level.some))
        case dir =>
          FileUtil[F].open(Paths.get(config.logDir), create = true, asDirectory = true) >>
            Logger.setRootHandlers(
              Logger.consoleHandler(level.some),
              fileHandler(Paths.get(dir), level.some, config.maxLogs)
            )
      })
  }

  def fileHandler(directory: Path, minimumLevel: Option[Level] = None, maxLogs: Int = 15): LogHandler = LogHandler(
    Logger.fileFormatter,
    FileWriter().nio
      .path(LogPath.simple("iotchain.log", directory = directory))
      .rolling(LogPath.daily(prefix = "iotchain", directory = directory))
      .maxLogs(maxLogs, checkRate = 1.seconds),
    minimumLevel.map(Logger.fromJbokLevel)
  )
} 
Example 25
Source File: ThreadUtil.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.common.thread

import java.lang.Thread.UncaughtExceptionHandler
import java.nio.channels.AsynchronousChannelGroup
import java.nio.channels.spi.AsynchronousChannelProvider
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.{Executors, ThreadFactory}

import cats.effect.{Resource, Sync}

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

object ThreadUtil {
  def named(threadPrefix: String, daemon: Boolean, exitJvmOnFatalError: Boolean = true): ThreadFactory =
    new ThreadFactory {
      val defaultThreadFactory = Executors.defaultThreadFactory()
      val idx                  = new AtomicInteger(0)
      def newThread(r: Runnable) = {
        val t = defaultThreadFactory.newThread(r)
        t.setDaemon(daemon)
        t.setName(s"$threadPrefix-${idx.incrementAndGet()}")
        t.setUncaughtExceptionHandler(new UncaughtExceptionHandler {
          def uncaughtException(t: Thread, e: Throwable): Unit = {
            ExecutionContext.defaultReporter(e)
            if (exitJvmOnFatalError) {
              e match {
                case NonFatal(_) => ()
                case _           => System.exit(-1)
              }
            }
          }
        })
        t
      }
    }

  def blockingThreadPool[F[_]](name: String)(implicit F: Sync[F]): Resource[F, ExecutionContext] =
    Resource(F.delay {
      val factory  = named(name, daemon = true)
      val executor = Executors.newCachedThreadPool(factory)
      val ec       = ExecutionContext.fromExecutor(executor)
      (ec, F.delay(executor.shutdown()))
    })

  def acg[F[_]](implicit F: Sync[F]): Resource[F, AsynchronousChannelGroup] =
    Resource(F.delay {
      val acg = acgUnsafe
      (acg, F.delay(acg.shutdownNow()))
    })

  def acgUnsafe: AsynchronousChannelGroup =
    AsynchronousChannelProvider
      .provider()
      .openAsynchronousChannelGroup(8, named("jbok-ag-tcp", daemon = true))

  lazy val acgGlobal: AsynchronousChannelGroup = acgUnsafe
} 
Example 26
Source File: Metrics.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.common.metrics

import cats.effect.{Resource, Sync, Timer}
import cats.implicits._
import fs2._

import scala.concurrent.duration._

trait EffectMetrics[F[_]] { self: Metrics[F] =>
  def observed[A](name: String, labels: String*)(fa: F[A])(implicit F: Sync[F], T: Timer[F]): F[A] =
    for {
      start   <- T.clock.monotonic(NANOSECONDS)
      attempt <- fa.attempt
      end     <- T.clock.monotonic(NANOSECONDS)
      elapsed = end - start
      a <- attempt match {
        case Left(e) =>
          self.observe(name, "failure" :: labels.toList: _*)(elapsed.toDouble) >> F.raiseError(e)
        case Right(a) =>
          self.observe(name, "success" :: labels.toList: _*)(elapsed.toDouble).as(a)
      }
    } yield a

  def monitored[A](name: String, labels: String*)(res: Resource[F, A])(implicit F: Sync[F]): Resource[F, A] = {
    val r = Resource {
      for {
        _ <- self.inc(name, labels: _*)(1.0)
      } yield () -> self.dec(name, labels: _*)(1.0)
    }

    r.flatMap(_ => res)
  }
}

trait StreamMetrics[F[_]] { self: Metrics[F] =>
  // observe events occur in the stream
  def observePipe[A](name: String, labels: String*): Pipe[F, A, Unit] =
    _.chunks.through(observeChunkPipe[A](name, labels: _*))

  def observeChunkPipe[A](name: String, labels: String*): Pipe[F, Chunk[A], Unit] =
    _.evalMap(c => self.observe(name, labels: _*)(c.size.toDouble))
}

trait Metrics[F[_]] extends EffectMetrics[F] with StreamMetrics[F] {
  type Registry

  def registry: Registry

  // accumulate, e.g. the number of requests served, tasks completed, or errors.
  def acc(name: String, labels: String*)(n: Double = 1.0): F[Unit]

  // increase, e.g. the current memory usage, queue size, or active requests.
  def inc(name: String, labels: String*)(n: Double = 1.0): F[Unit]

  // decrease, e.g. the current memory usage, queue size, or active requests.
  def dec(name: String, labels: String*)(n: Double = 1.0): F[Unit]

  // equivalent to inc(name, labels)(delta)
  def set(name: String, labels: String*)(n: Double): F[Unit]

  // e.g. the request response latency, or the size of the response body
  def observe(name: String, labels: String*)(n: Double): F[Unit]
}

object Metrics {
  val METRIC_PREFIX = "iotchain"
  val TIMER_SUFFIX  = "seconds"
  val GAUGE_SUFFIX  = "active"

  sealed trait NoopRegistry

  object NoopRegistry extends NoopRegistry

  def nop[F[_]: Sync]: Metrics[F] = new Metrics[F] {
    override type Registry = NoopRegistry
    override def registry: Registry                                         = NoopRegistry
    override def acc(name: String, labels: String*)(n: Double): F[Unit]     = Sync[F].unit
    override def inc(name: String, labels: String*)(n: Double): F[Unit]     = Sync[F].unit
    override def dec(name: String, labels: String*)(n: Double): F[Unit]     = Sync[F].unit
    override def set(name: String, labels: String*)(n: Double): F[Unit]     = Sync[F].unit
    override def observe(name: String, labels: String*)(n: Double): F[Unit] = Sync[F].unit
  }
} 
Example 27
Source File: MemoryKVStore.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.persistent

import cats.effect.Sync
import cats.effect.concurrent.Ref
import cats.implicits._
import fs2._

final class MemoryKVStore[F[_]](m: Ref[F, Map[ColumnFamily, Map[Seq[Byte], Array[Byte]]]])(implicit F: Sync[F]) extends KVStore[F] {
  override def put(cf: ColumnFamily, key: Array[Byte], value: Array[Byte]): F[Unit] =
    m.update(m => m.updated(cf, m.getOrElse(cf, Map.empty) + (key.toSeq -> value)))

  override def del(cf: ColumnFamily, key: Array[Byte]): F[Unit] =
    m.update(m => m.updated(cf, m.getOrElse(cf, Map.empty) - key))

  override def writeBatch(cf: ColumnFamily, puts: List[(Array[Byte], Array[Byte])], dels: List[Array[Byte]]): F[Unit] =
    for {
      _ <- puts.traverse { case (key, value) => put(cf, key, value) }
      _ <- dels.traverse { key =>
        del(cf, key)
      }
    } yield ()

  override def writeBatch(cf: ColumnFamily, ops: List[(Array[Byte], Option[Array[Byte]])]): F[Unit] =
    ops.traverse_ {
      case (key, Some(value)) => put(cf, key, value)
      case (key, None)        => del(cf, key)
    }

  override def writeBatch(puts: List[Put], dels: List[Del]): F[Unit] =
    for {
      _ <- puts.traverse { case (cf, key, value) => put(cf, key, value) }
      _ <- dels.traverse { case (cf, key)        => del(cf, key) }
    } yield ()

  override def get(cf: ColumnFamily, key: Array[Byte]): F[Option[Array[Byte]]] =
    m.get.map(_.get(cf).flatMap(_.get(key)))

  override def toStream(cf: ColumnFamily): Stream[F, (Array[Byte], Array[Byte])] =
    Stream.eval(toList(cf)).flatMap(Stream.emits)

  override def toList(cf: ColumnFamily): F[List[(Array[Byte], Array[Byte])]] =
    toMap(cf).map(_.toList)

  override def toMap(cf: ColumnFamily): F[Map[Array[Byte], Array[Byte]]] =
    m.get.map(_.getOrElse(cf, Map.empty).map { case (k, v) => k.toArray -> v })

  override def size(cf: ColumnFamily): F[Int] =
    m.get.map(_.get(cf).map(_.size).getOrElse(0))
}

object MemoryKVStore {
  def apply[F[_]](implicit F: Sync[F]): F[KVStore[F]] =
    Ref.of[F, Map[ColumnFamily, Map[Seq[Byte], Array[Byte]]]](Map.empty).map(ref => new MemoryKVStore[F](ref))
} 
Example 28
Source File: StageKVStore.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.persistent

import cats.effect.Sync
import cats.implicits._

final case class StageKVStore[F[_], K, V](inner: SingleColumnKVStore[F, K, V], stage: Map[K, Option[V]])(implicit F: Sync[F]) {
  def mustGet(key: K): F[V] =
    get(key).flatMap(opt => F.fromOption(opt, new Exception(s"fatal db failure, key=${key} not found")))

  def put(key: K, value: V): StageKVStore[F, K, V] = copy(stage = stage + (key -> Some(value)))

  def get(key: K): F[Option[V]] =
    stage.get(key) match {
      case Some(valueOpt) => valueOpt.pure[F]
      case None           => inner.get(key)
    }

  def del(key: K): StageKVStore[F, K, V] =
    copy(stage = stage + (key -> None))

  def toMap: F[Map[K, V]] =
    inner.toMap.map(kvs => kvs ++ stage.collect { case (k, Some(v)) => k -> v })

  def commit: F[StageKVStore[F, K, V]] =
    for {
      _ <- inner.writeBatch(stage.toList)
    } yield copy[F, K, V](stage = Map.empty)

  def +(kv: (K, V)): StageKVStore[F, K, V] =
    copy(stage = stage + (kv._1 -> Some(kv._2)))

  def ++(kvs: Map[K, V]): StageKVStore[F, K, V] =
    copy(stage = stage ++ kvs.mapValues(Some.apply))
}

object StageKVStore {
  def apply[F[_]: Sync, K, V](inner: SingleColumnKVStore[F, K, V]): StageKVStore[F, K, V] =
    StageKVStore[F, K, V](inner, Map.empty[K, Option[V]])
} 
Example 29
Source File: ProgramContext.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.evm

import cats.effect.Sync
import jbok.common.math.N
import jbok.core.models.{Address, BlockHeader, SignedTransaction, UInt256}
import scodec.bits.ByteVector

object ProgramContext {
  def apply[F[_]: Sync](
      stx: SignedTransaction,
      senderAddress: Address,
      recipientAddress: Address,
      program: Program,
      blockHeader: BlockHeader,
      world: WorldState[F],
      config: EvmConfig
  ): ProgramContext[F] = {

    // YP eq (91)
    val inputData =
      if (stx.isContractInit) ByteVector.empty
      else stx.payload

    val env = ExecEnv(
      recipientAddress,
      senderAddress,
      senderAddress,
      UInt256(stx.gasPrice),
      inputData,
      UInt256(stx.value),
      program,
      blockHeader,
      callDepth = 0
    )

    val gasLimit = stx.gasLimit - config.calcTransactionIntrinsicGas(stx.payload, stx.isContractInit)

    ProgramContext[F](env, recipientAddress, gasLimit, world, config)
  }
}


final case class ProgramContext[F[_]: Sync](
    env: ExecEnv,
    receivingAddr: Address,
    startGas: N,
    world: WorldState[F],
    config: EvmConfig,
    initialAddressesToDelete: Set[Address] = Set.empty,
    readOnly: Boolean = false
) 
Example 30
Source File: VM.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.evm

import cats.data.OptionT
import cats.effect.Sync
import cats.implicits._
import jbok.common.log.Logger


  def run[F[_]: Sync](context: ProgramContext[F]): F[ProgramResult[F]] = {
    val state = ProgramState[F](context)
    OptionT.fromOption[F](PrecompiledContracts.runOptionally(state.config.preCompiledContracts, context)).getOrElseF {
      run(state).map { finalState =>
        ProgramResult[F](
          finalState.returnData,
          finalState.gas,
          finalState.world,
          finalState.addressesToDelete,
          finalState.logs,
          finalState.internalTxs,
          finalState.gasRefund,
          finalState.error,
          finalState.reverted
        )
      }
    }
  }

  private def run[F[_]: Sync](state: ProgramState[F]): F[ProgramState[F]] = {
    val byte = state.program.getByte(state.pc)
    state.config.byteToOpCode.get(byte) match {
      case Some(opCode) =>
        for {
          newState <- opCode.execute(state)
          _ <- Logger[F].trace(
            s"$opCode | pc: ${newState.pc} | depth: ${newState.env.callDepth} | gas: ${newState.gas} | stack: ${newState.stack}")
          s <- if (newState.halted || newState.reverted) newState.pure[F] else run(newState)
        } yield s

      case None =>
        state.withError(InvalidOpCode(byte)).halt.pure[F]
    }
  }
} 
Example 31
Source File: Storage.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.evm

import cats.effect.Sync
import cats.implicits._
import jbok.core.models.UInt256
import jbok.core.store.ColumnFamilies
import jbok.persistent._

final case class Storage[F[_]: Sync](store: StageKVStore[F, UInt256, UInt256]) {
  def store(offset: UInt256, value: UInt256): F[Storage[F]] = Sync[F].pure {
    if (value == UInt256.zero) {
      this.copy(store = store.del(offset))
    } else {
      this.copy(store = store.put(offset, value))
    }
  }

  def load(offset: UInt256): F[UInt256] = store.get(offset).map(_.getOrElse(UInt256.zero))

  def commit: F[Storage[F]] = store.commit.map(db2 => this.copy(store = db2))

  def data: F[Map[UInt256, UInt256]] = store.toMap
}

object Storage {
  def empty[F[_]: Sync]: F[Storage[F]] =
    for {
      store <- MemoryKVStore[F]
      stage = StageKVStore(SingleColumnKVStore[F, UInt256, UInt256](ColumnFamilies.Node, store))
    } yield Storage[F](stage)

  def fromMap[F[_]: Sync](kvs: Map[UInt256, UInt256]): F[Storage[F]] =
    for {
      storage <- empty[F]
      stored <- kvs.toList.foldLeftM(storage) {
        case (s, (k, v)) =>
          s.store(k, v)
      }
    } yield stored

  def fromList[F[_]: Sync](words: List[UInt256]): F[Storage[F]] =
    for {
      storage <- empty[F]
      stored <- words.zipWithIndex.map { case (w, i) => UInt256(i) -> w }.foldLeftM(storage) {
        case (s, (k, v)) =>
          s.store(k, v)
      }
    } yield stored
} 
Example 32
Source File: TxValidator.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.core.validators

import cats.effect.Sync
import jbok.core.config.HistoryConfig
import jbok.core.models._
import jbok.core.validators.TxInvalid._
import jbok.evm.EvmConfig
import cats.implicits._
import jbok.common.math.N
import jbok.common.math.implicits._

object TxInvalid {
  final case object TxSignatureInvalid             extends Exception("SignedTransactionInvalid")
  final case class TxSyntaxInvalid(reason: String) extends Exception(s"TransactionSyntaxInvalid: ${reason}")
  final case class TxNonceTooHigh(txNonce: UInt256, senderNonce: UInt256)
      extends Exception(
        s"TxNonceTooHigh(got tx nonce $txNonce but sender nonce in mpt is: $senderNonce)"
      )
  final case class TxNonceInvalid(txNonce: UInt256, senderNonce: UInt256)
      extends Exception(
        s"TxNonceInvalid(got tx nonce $txNonce but sender nonce in mpt is: $senderNonce)"
      )
  final case class TxNotEnoughGasForIntrinsicInvalid(txGasLimit: N, txIntrinsicGas: N)
      extends Exception(
        s"TxNotEnoughGasForIntrinsicInvalid(xx gas limit ($txGasLimit) < tx intrinsic gas ($txIntrinsicGas))"
      )
  final case class TxSenderCantPayUpfrontCostInvalid(upfrontCost: UInt256, senderBalance: UInt256)
      extends Exception(
        s"TxSenderCantPayUpfrontCostInvalid(upfrontcost ($upfrontCost) > sender balance ($senderBalance))"
      )
  final case class TrxGasLimitTooBigInvalid(txGasLimit: N, accumGasUsed: N, blockGasLimit: N)
      extends Exception(
        s"TxGasLimitTooBigInvalid(tx gas limit ($txGasLimit) + acc gas ($accumGasUsed) > block gas limit ($blockGasLimit))"
      )
}

final class TxValidator[F[_]](historyConfig: HistoryConfig)(implicit F: Sync[F], chainId: ChainId) {
  import TxValidator._

  def validate(
      stx: SignedTransaction,
      senderAccount: Account,
      blockHeader: BlockHeader,
      upfrontGasCost: UInt256,
      accGasUsed: N
  ): F[Unit] =
    for {
      _ <- checkSyntacticValidity[F](stx, chainId)
      _ <- validateNonce(stx.nonce, senderAccount.nonce)
      _ <- validateGasLimitEnoughForIntrinsicGas(stx, blockHeader.number)
      _ <- validateAccountHasEnoughGasToPayUpfrontCost(senderAccount.balance, upfrontGasCost)
      _ <- validateBlockHasEnoughGasLimitForTx(stx.gasLimit, accGasUsed, blockHeader.gasLimit)
    } yield ()

  
  def checkSyntacticValidity[F[_]](stx: SignedTransaction, chainId: ChainId)(implicit F: Sync[F]): F[Unit] = {
    import stx._

    val validR = r > 0 && r < secp256k1n
    val validS = s > 0 && s < (secp256k1n / 2) + 1

    if (nonce > maxNonceValue)
      F.raiseError(TxSyntaxInvalid(s"Invalid nonce: $nonce > $maxNonceValue"))
    else if (gasLimit > maxGasValue)
      F.raiseError(TxSyntaxInvalid(s"Invalid gasLimit: $gasLimit > $maxGasValue"))
    else if (gasPrice > maxGasValue)
      F.raiseError(TxSyntaxInvalid(s"Invalid gasPrice: $gasPrice > $maxGasValue"))
    else if (value > maxValue)
      F.raiseError(TxSyntaxInvalid(s"Invalid value: $value > $maxValue"))
    else if (!validR)
      F.raiseError(TxSyntaxInvalid(s"Invalid r: ${r}"))
    else if (!validS)
      F.raiseError(TxSyntaxInvalid(s"Invalid s: ${s}"))
    else if (stx.senderAddress.isEmpty || !stx.chainIdOpt.contains(chainId))
      F.raiseError(TxSignatureInvalid)
    else
      F.unit
  }
} 
Example 33
Source File: BodyValidator.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.core.validators

import cats.effect.Sync
import cats.implicits._

import jbok.core.models._
import jbok.core.validators.BodyInvalid._
import jbok.persistent.mpt.MerklePatriciaTrie

object BodyInvalid {
  case object BodyTransactionsHashInvalid extends Exception("BlockTransactionsHashInvalid")
}

private[validators] object BodyValidator {

  
  def validate[F[_]: Sync](block: Block): F[Unit] =
    for {
      _ <- validateTransactionRoot(block)
    } yield ()

  private def validateTransactionRoot[F[_]](block: Block)(implicit F: Sync[F]): F[Unit] =
    MerklePatriciaTrie.calcMerkleRoot[F, SignedTransaction](block.body.transactionList).flatMap { root =>
      if (root == block.header.transactionsRoot) F.unit
      else F.raiseError(BodyTransactionsHashInvalid)
    }
} 
Example 34
Source File: Peer.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.core.peer

import cats.effect.concurrent.Ref
import cats.effect.{Concurrent, Sync}
import cats.implicits._
import fs2.concurrent.Queue
import jbok.core.messages.{SignedTransactions, Status}
import jbok.network.Message
import scodec.bits.ByteVector
import jbok.codec.rlp.implicits._
import jbok.common.log.Logger
import jbok.common.math.N
import jbok.crypto._

final case class Peer[F[_]](
    uri: PeerUri,
    queue: Queue[F, Message[F]],
    status: Ref[F, Status],
    knownBlocks: Ref[F, Set[ByteVector]],
    knownTxs: Ref[F, Set[ByteVector]]
)(implicit F: Sync[F]) {
  import Peer._

  private[this] val log = Logger[F]

  def hasBlock(blockHash: ByteVector): F[Boolean] =
    knownBlocks.get.map(_.contains(blockHash))

  def hasTxs(stxs: SignedTransactions): F[Boolean] =
    knownTxs.get.map(_.contains(stxs.encoded.bytes.kec256))

  def markBlock(blockHash: ByteVector, number: N): F[Unit] =
    knownBlocks.update(s => s.take(MaxKnownBlocks - 1) + blockHash) >>
      status.update(s => s.copy(bestNumber = s.bestNumber.max(number)))

  def markTxs(stxs: SignedTransactions): F[Unit] =
    knownTxs.update(known => known.take(MaxKnownTxs - 1) + stxs.encoded.bytes.kec256)

  def markStatus(newStatus: Status): F[Unit] =
    status.update(s => if (newStatus.td > s.td) s.copy(bestNumber = newStatus.bestNumber, td = newStatus.td) else s)
}

object Peer {
  val MaxKnownTxs    = 32768
  val MaxKnownBlocks = 1024

  def apply[F[_]: Concurrent](uri: PeerUri, status: Status): F[Peer[F]] =
    for {
      queue       <- Queue.circularBuffer[F, Message[F]](100000)
      status      <- Ref.of[F, Status](status)
      knownBlocks <- Ref.of[F, Set[ByteVector]](Set.empty)
      knownTxs    <- Ref.of[F, Set[ByteVector]](Set.empty)
    } yield Peer[F](uri, queue, status, knownBlocks, knownTxs)
} 
Example 35
Source File: PeerSelector.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.core.peer

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

import scala.util.Random

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

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

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

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

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

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

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

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

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

  def randomSelectSqrt[F[_]: Sync](min: Int): PeerSelector[F] = PeerSelector { peers =>
    val numberOfPeersToSend = math.max(math.sqrt(peers.size).toInt, min)
    Sync[F].pure(Random.shuffle(peers).take(numberOfPeersToSend))
  }
} 
Example 36
Source File: MiningConfig.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.core.config

import cats.effect.Sync
import cats.implicits._
import io.circe.generic.extras.ConfiguredJsonCodec
import jbok.core.keystore.KeyStore
import jbok.core.models.Address
import jbok.crypto.signature.KeyPair
import jbok.codec.json.implicits._

import scala.concurrent.duration.FiniteDuration

@ConfiguredJsonCodec
final case class MiningConfig(
    enabled: Boolean,
    address: Address,
    passphrase: String,
    coinbase: Address,
    period: FiniteDuration,
    epoch: Int,
    minBroadcastPeers: Int
)

object MiningConfig {
  def getKeyPair[F[_]](config: MiningConfig, keyStore: KeyStore[F])(implicit F: Sync[F]): F[Option[KeyPair]] =
    if (config.enabled) {
      keyStore.unlockAccount(config.coinbase, config.passphrase).map(_.keyPair.some)
    } else F.pure(None)
} 
Example 37
Source File: Wallet.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.core.keystore

import cats.effect.Sync
import cats.implicits._
import jbok.core.models.{Address, ChainId, SignedTransaction, Transaction}
import jbok.crypto.signature.{ECDSA, KeyPair, Signature}

final case class Wallet(address: Address, keyPair: KeyPair) {
  def signTx[F[_]: Sync](tx: Transaction, chainId: ChainId): F[SignedTransaction] =
    SignedTransaction.sign[F](tx, keyPair, chainId)
}

object Wallet {
  def fromSecret[F[_]: Sync](secret: KeyPair.Secret): F[Wallet] =
    for {
      public <- Signature[ECDSA].generatePublicKey[F](secret)
      keyPair = KeyPair(public, secret)
      address = Address(keyPair)
    } yield Wallet(address, keyPair)
} 
Example 38
Source File: MockingKeyStore.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.core.keystore

import cats.effect.Sync
import cats.implicits._
import cats.effect.concurrent.Ref
import jbok.core.models.Address
import jbok.crypto.signature.{ECDSA, KeyPair, Signature}
import scodec.bits.ByteVector

final class MockingKeyStore[F[_]](implicit F: Sync[F]) extends KeyStore[F] {
  private val m: Ref[F, Map[Address, KeyPair]] = Ref.unsafe(Map.empty)

  override def newAccount(passphrase: String): F[Address] =
    for {
      kp <- Signature[ECDSA].generateKeyPair[F]()
      _  <- m.update(_ + (Address(kp) -> kp))
    } yield Address(kp)

  override def readPassphrase(prompt: String): F[String] =
    ???

  override def importPrivateKey(key: ByteVector, passphrase: String): F[Address] =
    for {
      secret <- KeyPair.Secret(key).pure[F]
      public <- Signature[ECDSA].generatePublicKey[F](secret)
      kp      = KeyPair(public, secret)
      address = Address(kp)
      _ <- m.update(_ + (address -> kp))
    } yield address

  override def listAccounts: F[List[Address]] =
    m.get.map(_.keys.toList)

  override def unlockAccount(address: Address, passphrase: String): F[Wallet] =
    m.get.map(_(address)).map { kp =>
      Wallet(Address(kp), kp)
    }

  override def deleteAccount(address: Address): F[Boolean] =
    m.update(_ - address).as(true)

  override def changePassphrase(address: Address, oldPassphrase: String, newPassphrase: String): F[Boolean] =
    F.pure(true)
}

object MockingKeyStore {
  def withInitKeys[F[_]: Sync](initKeys: List[KeyPair]): F[MockingKeyStore[F]] = {
    val keystore = new MockingKeyStore[F]()
    initKeys.traverse(kp => keystore.importPrivateKey(kp.secret.bytes, "")).as(keystore)
  }
} 
Example 39
Source File: FUUIDGen.scala    From fuuid   with MIT License 5 votes vote down vote up
package io.chrisdavenport.fuuid

import java.util.UUID
import cats.implicits._
import cats.effect.Sync


  def nameBased(namespace: FUUID, name: String): F[FUUID]
}

object FUUIDGen {
  def apply[F[_]](implicit ev: FUUIDGen[F]): FUUIDGen[F] = ev

  // Sync f => class FUUIDGen f
  implicit def instance[F[_]: Sync]: FUUIDGen[F] = new SyncFUUIDGen[F]

  private class SyncFUUIDGen[F[_]: Sync] extends FUUIDGen[F]{
    def random: F[FUUID] = FUUID.randomFUUID[F]
    def fromString(s: String): F[FUUID] = FUUID.fromStringF[F](s)
    def fromUUID(uuid: UUID): F[FUUID] = FUUID.fromUUID(uuid).pure[F]
    def nameBased(namespace: FUUID, name: String): F[FUUID] = FUUID.nameBased[F](namespace, name)
  }
} 
Example 40
Source File: FUUID.scala    From fuuid   with MIT License 5 votes vote down vote up
package io.chrisdavenport.fuuid

import cats._
import cats.implicits._
import cats.effect.Sync
import java.util.UUID

import scala.reflect.macros.blackbox

final class FUUID private (private val uuid: UUID){

  // Direct show method so people do not use toString
  def show: String = uuid.show
  // -1 less than, 0 equal to, 1 greater than
  def compare(that: FUUID): Int = this.uuid.compareTo(that.uuid)

  // Returns 0 when equal
  def eqv(that: FUUID): Boolean = compare(that) == 0

  override def equals(obj: scala.Any): Boolean = obj match {
    case that: FUUID => eqv(that)
    case _ => false
  }
  override def hashCode: Int = uuid.hashCode
  override def toString: String = uuid.toString

}

object FUUID {
  implicit val instancesFUUID: Hash[FUUID] with Order[FUUID] with Show[FUUID] =
    new Hash[FUUID] with Order[FUUID] with Show[FUUID]{
      override def show(t: FUUID): String = t.show
      override def eqv(x: FUUID, y: FUUID): Boolean = x.eqv(y)
      override def hash(x: FUUID): Int = x.hashCode
      override def compare(x: FUUID, y: FUUID): Int = x.compare(y)
    }

  def fromString(s: String): Either[Throwable, FUUID] =
    Either.catchNonFatal(new FUUID(UUID.fromString(s)))

  def fromStringOpt(s: String): Option[FUUID] = 
    fromString(s).toOption

  def fromStringF[F[_]](s: String)(implicit AE: ApplicativeError[F, Throwable]): F[FUUID] =
    fromString(s).fold(AE.raiseError, AE.pure)

  def fromUUID(uuid: UUID): FUUID = new FUUID(uuid)

  def randomFUUID[F[_]: Sync]: F[FUUID] = Sync[F].delay(
    new FUUID(UUID.randomUUID)
  )

  def fuuid(s: String): FUUID = macro Macros.fuuidLiteral

  private[FUUID] class Macros(val c: blackbox.Context) {
    import c.universe._
    def fuuidLiteral(s: c.Expr[String]): c.Expr[FUUID] =
      s.tree match {
        case Literal(Constant(s: String))=>
            fromString(s)
            .fold(
              e => c.abort(c.enclosingPosition, e.getMessage.replace("UUID", "FUUID")),
              _ => c.Expr(q"""
                @SuppressWarnings(Array("org.wartremover.warts.Throw"))
                val fuuid = _root_.io.chrisdavenport.fuuid.FUUID.fromString($s).fold(throw _, _root_.scala.Predef.identity)
                fuuid
              """)
            )
        case _ =>
          c.abort(
            c.enclosingPosition,
            s"This method uses a macro to verify that a FUUID literal is valid. Use FUUID.fromString if you have a dynamic value you want to parse as an FUUID."
          )
      }
  }

  
  object Unsafe {
    def toUUID(fuuid: FUUID): UUID = fuuid.uuid
    def withUUID[A](fuuid: FUUID)(f: UUID => A): A = f(fuuid.uuid)
  }

} 
Example 41
Source File: FTracing.scala    From opencensus-scala   with Apache License 2.0 5 votes vote down vote up
package io.opencensus.scala.doobie

import cats.effect.ExitCase.{Canceled, Completed, Error}
import cats.effect.{Bracket, Sync}
import doobie.free.connection.ConnectionIO
import io.opencensus.scala.Tracing
import io.opencensus.scala.doobie.FTracing.FBracket
import io.opencensus.trace.{Span, Status}
import cats.syntax.flatMap._
import cats.syntax.functor._

abstract class FTracing[F[_]: Sync: FBracket] {

  protected val tracing: Tracing

  private def fTrace(name: String, parentSpan: Option[Span]): F[Span] =
    Sync[F].delay(
      parentSpan.fold(tracing.startSpan(name))(span =>
        tracing.startSpanWithParent(name, span)
      )
    )

  private def fStop(span: Span): F[Unit] =
    Sync[F].delay(tracing.endSpan(span, Status.OK))

  private def fStopError(span: Span): F[Unit] =
    Sync[F].delay(tracing.endSpan(span, Status.INTERNAL))

  private def fStopCanceled(span: Span): F[Unit] =
    Sync[F].delay(tracing.endSpan(span, Status.CANCELLED))

  def traceF[A](co: F[A], name: String, parentSpan: Option[Span]): F[A] =
    for {
      startedSpan <- fTrace(name, parentSpan)
      result <- Bracket[F, Throwable].guaranteeCase(co) {
        case Completed => fStop(startedSpan)
        case Error(_)  => fStopError(startedSpan)
        case Canceled  => fStopCanceled(startedSpan)
      }
    } yield result
}

object FTracing {
  type FBracket[F[_]] = Bracket[F, Throwable]
}

object ConnectionIOTracing extends FTracing[ConnectionIO] {
  override protected val tracing: Tracing = Tracing
} 
Example 42
Source File: http4s.scala    From sup   with Apache License 2.0 5 votes vote down vote up
package sup.modules

import cats.effect.Sync
import cats.Monad
import cats.Reducible
import org.http4s.dsl.Http4sDsl
import org.http4s.EntityEncoder
import org.http4s.HttpRoutes
import org.http4s.Response
import sup.HealthCheck
import sup.HealthResult
import cats.implicits._

object http4s {

  
  def healthCheckRoutes[F[_]: Sync, H[_]: Reducible](
    healthCheck: HealthCheck[F, H],
    path: String = "health-check"
  )(
    implicit encoder: EntityEncoder[F, HealthResult[H]]
  ): HttpRoutes[F] = {

    val dsl = new Http4sDsl[F] {}
    import dsl._

    HttpRoutes.of[F] {
      case GET -> Root / `path` =>
        healthCheckResponse(healthCheck)
    }
  }

  def healthCheckResponse[F[_]: Monad, H[_]: Reducible](
    healthCheck: HealthCheck[F, H]
  )(
    implicit encoder: EntityEncoder[F, HealthResult[H]]
  ): F[Response[F]] = {

    val dsl = new Http4sDsl[F] {}
    import dsl._

    healthCheck.check.flatMap { check =>
      if (check.value.reduce.isHealthy) Ok(check)
      else ServiceUnavailable(check)
    }
  }
} 
Example 43
Source File: sttp.scala    From sup   with Apache License 2.0 5 votes vote down vote up
package sup.modules

import cats.Functor
import cats.effect.Sync
import com.softwaremill.sttp.{Id, Request, SttpBackend}
import sup.{HealthCheck, HealthResult}
import cats.implicits._

object sttp {

  
  def remoteHealthCheck[F[_]: Sync, H[_]](
    call: Request[HealthResult[H], Nothing]
  )(
    implicit backend: SttpBackend[F, Nothing]
  ): HealthCheck[F, H] =
    HealthCheck.liftF {
      backend.send(call).flatMap(response => Sync[F].delay(response.unsafeBody))
    }
} 
Example 44
Source File: Github.scala    From sonar-scala   with GNU Lesser General Public License v3.0 5 votes vote down vote up
package com.mwz.sonar.scala
package pr
package github

import cats.effect.Sync
import cats.syntax.flatMap._
import com.mwz.sonar.scala.pr.github.Codec._
import io.circe.generic.auto._
import mouse.boolean._
import org.http4s.client.Client
import org.http4s.{Header, Headers, Method, Request, Uri}

trait Github[F[_]] {
  def authenticatedUser: F[User]
  def pullRequest: F[PullRequest]
  def comments: F[List[Comment]]
  def createComment(comment: NewComment): F[Unit]
  def files: F[List[File]]
  def createStatus(sha: String, status: NewStatus): F[Unit]
}

object Github {
  def apply[F[_]: Sync](client: Client[F], pr: GlobalConfig.PullRequest): Github[F] =
    new Github[F] {
      val auth: Header = Header("Authorization", s"token ${pr.github.oauth}")
      val userUri: Uri = pr.github.apiuri / "user"
      val prUri: Uri =
        (pr.github.apiuri / "repos").addPath(pr.github.repository) / "pulls" / pr.prNumber
      val commentsUri: Uri = prUri / "comments"
      val filesUri: Uri = prUri / "files"
      def newStatusUri(sha: String): Uri =
        (pr.github.apiuri / "repos").addPath(pr.github.repository) / "statuses" / sha
      def request(uri: Uri): Request[F] = {
        Request[F](
          uri = uri,
          headers = Headers.of(auth)
        )
      }
      def authenticatedUser: F[User] = client.expect[User](request(userUri))
      def pullRequest: F[PullRequest] = client.expect[PullRequest](request(prUri))
      def comments: F[List[Comment]] = client.expect[List[Comment]](request(commentsUri))
      def createComment(comment: NewComment): F[Unit] = {
        val request: F[Request[F]] = Sync[F].pure(
          Request(Method.POST, commentsUri, headers = Headers.of(auth))
            .withEntity(comment)
        )
        pr.dryRun.fold(Sync[F].unit, client.expect[Comment](request) >> Sync[F].unit)
      }
      def files: F[List[File]] = client.expect[List[File]](request(filesUri))
      def createStatus(sha: String, status: NewStatus): F[Unit] = {
        val request: F[Request[F]] = Sync[F].pure(
          Request(Method.POST, newStatusUri(sha), headers = Headers.of(auth))
            .withEntity(status)
        )
        pr.dryRun.fold(Sync[F].unit, client.expect[Status](request) >> Sync[F].unit)
      }
    }
} 
Example 45
Source File: Logger.scala    From sonar-scala   with GNU Lesser General Public License v3.0 5 votes vote down vote up
package com.mwz.sonar.scala
package util

import cats.effect.Sync
import org.sonar.api.utils.log.{Logger => SonarLogger, Loggers => SonarLoggers}

trait Logger[F[_]] {
  def debug(s: String): F[Unit]
  def info(s: String): F[Unit]
  def warn(s: String): F[Unit]
  def error(s: String): F[Unit]
  def error(s: String, e: Throwable): F[Unit]
}

object Logger {
  def apply[F[_]](implicit ev: Logger[F]): Logger[F] = ev
  def create[F[_]: Sync, T](clazz: Class[T], module: String): F[Logger[F]] =
    create(clazz, Some(module))
  def create[F[_]: Sync, T](clazz: Class[T], module: Option[String] = None): F[Logger[F]] =
    Sync[F].delay {
      val log: SonarLogger = SonarLoggers.get(clazz)
      val prefix: String = "sonar-scala" + module.fold("")("-" + _)

      new Logger[F] {
        override def debug(s: String): F[Unit] =
          Sync[F].delay(log.debug(s"[$prefix] $s"))
        override def info(s: String): F[Unit] =
          Sync[F].delay(log.info(s"[$prefix] $s"))
        override def warn(s: String): F[Unit] =
          Sync[F].delay(log.warn(s"[$prefix] $s"))
        override def error(s: String): F[Unit] =
          Sync[F].delay(log.error(s"[$prefix] $s"))
        override def error(s: String, e: Throwable): F[Unit] =
          Sync[F].delay(log.error(s"[$prefix] $s", e))
      }
    }
} 
Example 46
Source File: GcsStore.scala    From fs2-blobstore   with Apache License 2.0 5 votes vote down vote up
package blobstore.gcs

import java.nio.channels.Channels
import java.time.Instant
import java.util.Date

import blobstore.{Path, Store}
import cats.effect.{Blocker, ContextShift, Sync}
import com.google.api.gax.paging.Page
import com.google.cloud.storage.{Acl, Blob, BlobId, BlobInfo, Storage}
import com.google.cloud.storage.Storage.{BlobListOption, CopyRequest}
import fs2.{Chunk, Pipe, Stream}

import scala.jdk.CollectionConverters._

final class GcsStore[F[_]](storage: Storage, blocker: Blocker, acls: List[Acl] = Nil)(implicit F: Sync[F], CS: ContextShift[F]) extends Store[F] {

  private def _chunk(pg: Page[Blob]): Chunk[Path] = {
    val (dirs, files) = pg.getValues.asScala.toSeq.partition(_.isDirectory)
    val dirPaths = Chunk.seq(dirs.map(b => Path(root = b.getBucket, key = b.getName.stripSuffix("/"), size = None, isDir = true, lastModified = None)))
    val filePaths = Chunk.seq(files.map{b =>
      val size = Option(b.getSize: java.lang.Long).map(_.toLong) // Prevent throwing NPE (see https://github.com/scala/bug/issues/9634)
      val lastModified = Option(b.getUpdateTime: java.lang.Long).map(millis => Date.from(Instant.ofEpochMilli(millis))) // Prevent throwing NPE (see https://github.com/scala/bug/issues/9634)
      Path(b.getBucket, key = b.getName, size = size, isDir = false, lastModified = lastModified)
    })
    Chunk.concat(List(dirPaths, filePaths))
  }

  def list(path: Path): fs2.Stream[F, Path] = {
    Stream.unfoldChunkEval[F, () => Option[Page[Blob]], Path]{
      () => Some(storage.list(path.root, BlobListOption.currentDirectory(), BlobListOption.prefix(path.key)))
    }{getPage =>
      blocker.delay{
        getPage().map{pg =>
          if (pg.hasNextPage){
            (_chunk(pg), () => Some(pg.getNextPage))
          } else {
            (_chunk(pg), () => None)
          }
        }
      }
    }
  }

  def get(path: Path, chunkSize: Int): fs2.Stream[F, Byte] = {
    val is = blocker.delay(Channels.newInputStream(storage.get(path.root, path.key).reader()))
    fs2.io.readInputStream(is, chunkSize, blocker, closeAfterUse = true)
  }

  def put(path: Path): Pipe[F, Byte, Unit] = {
    val fos = Sync[F].delay{
      val builder = {
        val b = BlobInfo.newBuilder(path.root, path.key)
        if (acls.nonEmpty) b.setAcl(acls.asJava) else b
      }
      val blobInfo = builder.build()
      val writer = storage.writer(blobInfo)
      Channels.newOutputStream(writer)
    }
    fs2.io.writeOutputStream(fos, blocker, closeAfterUse = true)
  }

  def move(src: Path, dst: Path): F[Unit] = F.productR(copy(src, dst))(remove(src))

  def copy(src: Path, dst: Path): F[Unit] = {
    val req = CopyRequest.newBuilder().setSource(src.root, src.key).setTarget(BlobId.of(dst.root, dst.key)).build()
    F.void(blocker.delay(storage.copy(req).getResult))
  }

  def remove(path: Path): F[Unit] =
    F.void(blocker.delay(storage.delete(path.root, path.key)))
}


object GcsStore{
  def apply[F[_]](
    storage: Storage,
    blocker: Blocker,
    acls: List[Acl]
  )(implicit F: Sync[F], CS: ContextShift[F]): GcsStore[F] = new GcsStore(storage, blocker, acls)
} 
Example 47
Source File: FileStore.scala    From fs2-blobstore   with Apache License 2.0 5 votes vote down vote up
package blobstore
package fs

import java.nio.file.{Files, Paths, Path => NioPath}
import java.util.Date

import scala.jdk.CollectionConverters._
import cats.implicits._
import cats.effect.{Blocker, ContextShift, Sync}
import fs2.{Pipe, Stream}

final class FileStore[F[_]](fsroot: NioPath, blocker: Blocker)(implicit F: Sync[F], CS: ContextShift[F]) extends Store[F] {
  val absRoot: String = fsroot.toAbsolutePath.normalize.toString

  override def list(path: Path): fs2.Stream[F, Path] = {
    val isDir = Stream.eval(F.delay(Files.isDirectory(path)))
    val isFile = Stream.eval(F.delay(Files.exists(path)))

    val files = Stream.eval(F.delay(Files.list(path)))
      .flatMap(x => Stream.fromIterator(x.iterator.asScala))
      .evalMap(x => F.delay(
        Path(x.toAbsolutePath.toString.replaceFirst(absRoot, "")).copy(
          size = Option(Files.size(x)),
          isDir = Files.isDirectory(x),
          lastModified = Option(new Date(Files.getLastModifiedTime(path).toMillis))
        )
      ))

    val file = fs2.Stream.eval {
      F.delay {
        path.copy(
          size = Option(Files.size(path)),
          lastModified = Option(new Date(Files.getLastModifiedTime(path).toMillis))
        )
      }
    }

    isDir.ifM(files, isFile.ifM(file, Stream.empty.covaryAll[F, Path]))
  }

  override def get(path: Path, chunkSize: Int): fs2.Stream[F, Byte] = fs2.io.file.readAll[F](path, blocker, chunkSize)

  override def put(path: Path): Pipe[F, Byte, Unit] = { in =>
    val mkdir = Stream.eval(F.delay(Files.createDirectories(_toNioPath(path).getParent)).as(true))
    mkdir.ifM(
      fs2.io.file.writeAll(path, blocker).apply(in),
      Stream.raiseError[F](new Exception(s"failed to create dir: $path"))
    )
  }

  override def move(src: Path, dst: Path): F[Unit] = F.delay {
    Files.createDirectories(_toNioPath(dst).getParent)
    Files.move(src, dst)
  }.void

  override def copy(src: Path, dst: Path): F[Unit] = {
    F.delay {
      Files.createDirectories(_toNioPath(dst).getParent)
      Files.copy(src, dst)
    }.void
  }

  override def remove(path: Path): F[Unit] = F.delay({
    Files.deleteIfExists(path)
    ()
  })

  implicit private def _toNioPath(path: Path): NioPath =
    Paths.get(absRoot, path.root, path.key)

}

object FileStore{
  def apply[F[_]](fsroot: NioPath, blocker: Blocker)(implicit F: Sync[F], CS: ContextShift[F]): FileStore[F] = new FileStore(fsroot, blocker)
} 
Example 48
Source File: package.scala    From fs2-blobstore   with Apache License 2.0 5 votes vote down vote up
import java.io.OutputStream
import java.nio.file.Files

import cats.effect.{ContextShift, Sync, Blocker}
import fs2.{Pipe, Pull, Stream}
import cats.implicits._

package object blobstore {
  protected[blobstore] def _writeAllToOutputStream1[F[_]](in: Stream[F, Byte], out: OutputStream, blocker: Blocker)(
    implicit F: Sync[F], CS: ContextShift[F]): Pull[F, Nothing, Unit] = {
    in.pull.uncons.flatMap {
      case None => Pull.done
      case Some((hd, tl)) => Pull.eval[F, Unit](blocker.delay(out.write(hd.toArray))) >> _writeAllToOutputStream1(tl, out, blocker)
    }
  }

  protected[blobstore] def bufferToDisk[F[_]](chunkSize: Int, blocker: Blocker)(implicit F: Sync[F], CS: ContextShift[F])
  : Pipe[F, Byte, (Long, Stream[F, Byte])] = {
    in => Stream.bracket(F.delay(Files.createTempFile("bufferToDisk", ".bin")))(
      p => F.delay(p.toFile.delete).void).flatMap { p =>
        in.through(fs2.io.file.writeAll(p, blocker)).drain ++
        Stream.emit((p.toFile.length, fs2.io.file.readAll(p, blocker, chunkSize)))
    }
  }

} 
Example 49
Source File: Bootstrap.scala    From hydra   with Apache License 2.0 5 votes vote down vote up
package hydra.ingest.modules

import java.time.Instant

import cats.data.NonEmptyList
import cats.effect.Sync
import cats.implicits._
import cats.{Monad, MonadError}
import hydra.core.marshallers.History
import hydra.ingest.app.AppConfig.V2MetadataTopicConfig
import hydra.kafka.model._
import hydra.kafka.programs.CreateTopicProgram
import hydra.kafka.util.KafkaUtils.TopicDetails

final class Bootstrap[F[_]: MonadError[*[_], Throwable]] private (
    createTopicProgram: CreateTopicProgram[F],
    cfg: V2MetadataTopicConfig
) {

  def bootstrapAll: F[Unit] =
    for {
      _ <- bootstrapMetadataTopic
    } yield ()

  private def bootstrapMetadataTopic: F[Unit] =
    if (cfg.createOnStartup) {
      TopicMetadataV2.getSchemas[F].flatMap { schemas =>
        createTopicProgram.createTopic(
          cfg.topicName,
          TopicMetadataV2Request(
            schemas,
            StreamTypeV2.Entity,
            false,
            InternalUseOnly,
            NonEmptyList.of(cfg.contactMethod),
            Instant.now,
            List.empty,
            Some(
              "This is the topic that Hydra uses to keep track of metadata for topics."
            )
          ),
          TopicDetails(cfg.numPartitions, cfg.replicationFactor)
        )
      }
    } else {
      Monad[F].unit
    }

}

object Bootstrap {

  def make[F[_]: Sync](
      createTopicProgram: CreateTopicProgram[F],
      v2MetadataTopicConfig: V2MetadataTopicConfig
  ): F[Bootstrap[F]] = Sync[F].delay {
    new Bootstrap[F](createTopicProgram, v2MetadataTopicConfig)
  }
} 
Example 50
Source File: Routes.scala    From hydra   with Apache License 2.0 5 votes vote down vote up
package hydra.ingest.modules

import akka.actor.ActorSystem
import akka.http.scaladsl.server.directives.RouteDirectives
import akka.http.scaladsl.server.{Route, RouteConcatenation}
import cats.effect.Sync
import hydra.common.config.ConfigSupport
import hydra.common.util.{ActorUtils, Futurable}
import hydra.ingest.app.AppConfig.AppConfig
import hydra.ingest.http._
import hydra.kafka.consumer.KafkaConsumerProxy
import hydra.kafka.endpoints.{BootstrapEndpoint, BootstrapEndpointV2, TopicMetadataEndpoint, TopicsEndpoint}
import hydra.kafka.util.KafkaUtils.TopicDetails

import scala.concurrent.ExecutionContext

final class Routes[F[_]: Sync: Futurable] private(programs: Programs[F], algebras: Algebras[F], cfg: AppConfig)
                                                 (implicit system: ActorSystem) extends RouteConcatenation with ConfigSupport {

  private implicit val ec: ExecutionContext = system.dispatcher
  private val bootstrapEndpointV2 = if (cfg.v2MetadataTopicConfig.createV2TopicsEnabled) {
    val topicDetails =
      TopicDetails(
        cfg.createTopicConfig.defaultNumPartions,
        cfg.createTopicConfig.defaultReplicationFactor
      )
    new BootstrapEndpointV2(programs.createTopic, topicDetails).route
  } else {
    RouteDirectives.reject
  }

  lazy val routes: F[Route] = Sync[F].delay {
    import ConfigSupport._

    //TODO: remove this lookup
    val consumerPath = applicationConfig
      .getStringOpt("actors.kafka.consumer_proxy.path")
      .getOrElse(
        s"/user/service/${ActorUtils.actorName(classOf[KafkaConsumerProxy])}"
      )

    val consumerProxy = system.actorSelection(consumerPath)

    new SchemasEndpoint().route ~
      new BootstrapEndpoint(system).route ~
      new TopicMetadataEndpoint(consumerProxy, algebras.metadata).route ~
      new IngestorRegistryEndpoint().route ~
      new IngestionWebSocketEndpoint().route ~
      new IngestionEndpoint(cfg.ingestConfig.alternateIngestEnabled,
                            programs.ingestionFlow,
                            programs.ingestionFlowV2,
                            cfg.ingestConfig.useOldIngestIfUAContains).route ~
      new TopicsEndpoint(consumerProxy)(system.dispatcher).route ~
      HealthEndpoint.route ~
      bootstrapEndpointV2
  }
}

object Routes {
  def make[F[_]: Sync: Futurable](programs: Programs[F], algebras: Algebras[F], config: AppConfig)
                           (implicit system: ActorSystem): F[Routes[F]] = Sync[F].delay(new Routes[F](programs, algebras, config))
} 
Example 51
Source File: KafkaAdminAlgebra.scala    From hydra   with Apache License 2.0 5 votes vote down vote up
package hydra.kafka.algebras

import cats.effect.concurrent.Ref
import cats.effect.{Async, Concurrent, ContextShift, Resource, Sync}
import cats.implicits._
import fs2.kafka._
import hydra.core.protocol._
import hydra.kafka.util.KafkaUtils.TopicDetails
import org.apache.kafka.clients.admin.NewTopic
import org.apache.kafka.common.errors.UnknownTopicOrPartitionException

import scala.util.control.NoStackTrace


  def deleteTopic(name: String): F[Unit]
}

object KafkaAdminAlgebra {

  type TopicName = String
  final case class Topic(name: TopicName, numberPartitions: Int)

  def live[F[_]: Sync: Concurrent: ContextShift](
      bootstrapServers: String,
  ): F[KafkaAdminAlgebra[F]] = Sync[F].delay {
    new KafkaAdminAlgebra[F] {

      override def describeTopic(name: TopicName): F[Option[Topic]] = {
        getAdminClientResource
          .use(_.describeTopics(name :: Nil))
          .map(_.headOption.map(_._2).map { td =>
            Topic(td.name(), td.partitions().size())
          })
          .recover {
            case _: UnknownTopicOrPartitionException => None
          }
      }

      override def getTopicNames: F[List[TopicName]] =
        getAdminClientResource.use(_.listTopics.names.map(_.toList))

      override def createTopic(name: TopicName, d: TopicDetails): F[Unit] = {
        import scala.collection.JavaConverters._
        val newTopic = new NewTopic(name, d.numPartitions, d.replicationFactor)
          .configs(d.configs.asJava)
        getAdminClientResource.use(_.createTopic(newTopic))
      }

      override def deleteTopic(name: String): F[Unit] =
        getAdminClientResource.use(_.deleteTopic(name))

      private def getAdminClientResource: Resource[F, KafkaAdminClient[F]] = {
        adminClientResource(
          AdminClientSettings.apply.withBootstrapServers(bootstrapServers)
        )
      }
    }
  }

  def test[F[_]: Sync]: F[KafkaAdminAlgebra[F]] =
    Ref[F].of(Map[TopicName, Topic]()).flatMap(getTestKafkaClient[F])

  private[this] def getTestKafkaClient[F[_]: Sync](
      ref: Ref[F, Map[TopicName, Topic]]
  ): F[KafkaAdminAlgebra[F]] = Sync[F].delay {
    new KafkaAdminAlgebra[F] {
      override def describeTopic(name: TopicName): F[Option[Topic]] =
        ref.get.map(_.get(name))

      override def getTopicNames: F[List[TopicName]] =
        ref.get.map(_.keys.toList)

      override def createTopic(
          name: TopicName,
          details: TopicDetails
      ): F[Unit] = {
        val entry = name -> Topic(name, details.numPartitions)
        ref.update(old => old + entry)
      }

      override def deleteTopic(name: String): F[Unit] =
        ref.update(_ - name)
    }
  }

} 
Example 52
Source File: MetadataAlgebraSpec.scala    From hydra   with Apache License 2.0 5 votes vote down vote up
package hydra.kafka.algebras

import java.time.Instant

import cats.data.NonEmptyList
import cats.effect.{Concurrent, ContextShift, IO, Sync, Timer}
import cats.implicits._
import hydra.avro.registry.SchemaRegistry
import hydra.core.marshallers.History
import hydra.kafka.algebras.MetadataAlgebra.TopicMetadataContainer
import hydra.kafka.model.ContactMethod.Slack
import hydra.kafka.model.TopicMetadataV2Request.Subject
import hydra.kafka.model.{Public, StreamTypeV2, TopicMetadataV2, TopicMetadataV2Key, TopicMetadataV2Request, TopicMetadataV2Value}
import io.chrisdavenport.log4cats.SelfAwareStructuredLogger
import io.chrisdavenport.log4cats.slf4j.Slf4jLogger
import org.apache.avro.generic.GenericRecord
import org.scalatest.Assertion
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike
import retry.RetryPolicies._
import retry.syntax.all._
import retry.{RetryPolicy, _}

import scala.concurrent.ExecutionContext
import scala.concurrent.duration._

class MetadataAlgebraSpec extends AnyWordSpecLike with Matchers {

  implicit private val contextShift: ContextShift[IO] = IO.contextShift(ExecutionContext.global)
  private implicit val concurrentEffect: Concurrent[IO] = IO.ioConcurrentEffect

  private implicit val policy: RetryPolicy[IO] = limitRetries[IO](5) |+| exponentialBackoff[IO](500.milliseconds)
  private implicit val timer: Timer[IO] = IO.timer(ExecutionContext.global)
  private implicit def noop[A]: (A, RetryDetails) => IO[Unit] = retry.noop[IO, A]

  implicit private def unsafeLogger[F[_]: Sync]: SelfAwareStructuredLogger[F] =
    Slf4jLogger.getLogger[F]

  private implicit class RetryAndAssert[A](boolIO: IO[A]) {
    def retryIfFalse(check: A => Boolean): IO[Assertion] =
      boolIO.map(check).retryingM(identity, policy, noop).map(assert(_))
  }


  private val metadataTopicName = "_internal.metadataTopic"
  private val consumerGroup = "Consumer Group"

  (for {
    kafkaClient <- KafkaClientAlgebra.test[IO]
    schemaRegistry <- SchemaRegistry.test[IO]
    metadata <- MetadataAlgebra.make(metadataTopicName, consumerGroup, kafkaClient, schemaRegistry, consumeMetadataEnabled = true)
  } yield {
    runTests(metadata, kafkaClient)
  }).unsafeRunSync()

  private def runTests(metadataAlgebra: MetadataAlgebra[IO], kafkaClientAlgebra: KafkaClientAlgebra[IO]): Unit = {
    "MetadataAlgebraSpec" should {

      "retrieve none for non-existant topic" in {
        val subject = Subject.createValidated("Non-existantTopic").get
        metadataAlgebra.getMetadataFor(subject).unsafeRunSync() shouldBe None
      }

      "retrieve metadata" in {
        val subject = Subject.createValidated("subject1").get
        val (genericRecordsIO, key, value) = getMetadataGenericRecords(subject)

        (for {
          record <- genericRecordsIO
          _ <- kafkaClientAlgebra.publishMessage(record, metadataTopicName)
          _ <- metadataAlgebra.getMetadataFor(subject).retryIfFalse(_.isDefined)
          metadata <- metadataAlgebra.getMetadataFor(subject)
        } yield metadata shouldBe Some(TopicMetadataContainer(key, value, None, None))).unsafeRunSync()
      }

      "retrieve all metadata" in {
        val subject = Subject.createValidated("subject2").get
        val (genericRecordsIO, key, value) = getMetadataGenericRecords(subject)
        (for {
          record <- genericRecordsIO
          _ <- kafkaClientAlgebra.publishMessage(record, metadataTopicName)
          _ <- metadataAlgebra.getMetadataFor(subject).retryIfFalse(_.isDefined)
          allMetadata <- metadataAlgebra.getAllMetadata
        } yield allMetadata should have length 2).unsafeRunSync()
      }
    }
  }

  private def getMetadataGenericRecords(subject: Subject): (IO[(GenericRecord, Option[GenericRecord])], TopicMetadataV2Key, TopicMetadataV2Value) = {
    val key = TopicMetadataV2Key(subject)
    val value = TopicMetadataV2Value(
        StreamTypeV2.Entity,
        deprecated = false,
        Public,
        NonEmptyList.one(Slack.create("#channel").get),
        Instant.now,
        List(),
        None)
    (TopicMetadataV2.encode[IO](key, Some(value)), key, value)
  }
} 
Example 53
Source File: OutWatch.scala    From outwatch   with Apache License 2.0 5 votes vote down vote up
package outwatch

import cats.effect.Sync
import cats.implicits._
import org.scalajs.dom
import org.scalajs.dom._
import outwatch.interpreter.SnabbdomOps
import snabbdom.{VNodeProxy, patch}

object OutWatch {
  def toSnabbdom[F[_]](vNode: VNode)(implicit F: Sync[F]): F[VNodeProxy] = F.delay {
    SnabbdomOps.toSnabbdom(vNode)
  }

  def renderInto[F[_]](element: dom.Element, vNode: VNode)(implicit F: Sync[F]): F[Unit] =
    toSnabbdom(vNode).map { node =>
      val elem = dom.document.createElement("div")
      element.appendChild(elem)
      patch(elem, node)
    }.void

  def renderReplace[F[_]: Sync](element: dom.Element, vNode: VNode): F[Unit] =
    toSnabbdom(vNode).map { node =>
      val elementNode = snabbdom.tovnode(element)
      patch(elementNode, node)
    }.void

  def renderInto[F[_]: Sync](querySelector: String, vNode: VNode): F[Unit] =
    renderInto(document.querySelector(querySelector), vNode)

  def renderReplace[F[_]: Sync](querySelector: String, vNode: VNode): F[Unit] =
    renderReplace(document.querySelector(querySelector), vNode)
} 
Example 54
Source File: HandlerFactory.scala    From outwatch   with Apache License 2.0 5 votes vote down vote up
package outwatch.reactive

import cats.effect.{Sync, SyncIO}
import colibri._

@inline final class HandlerFactory[H[_] : CreateSubject] {
  // Create a Handler that keeps the last emitted value as State, typically a BehaviourSubject or ReplaySubject

  @inline def create[T]: SyncIO[H[T]] = SyncIO(unsafe[T])
  @inline def create[T](seed: T): SyncIO[H[T]] = SyncIO(unsafe[T](seed))

  @inline def createF[F[_]] = new CreatePartiallyApplied[F]
  @inline def createF[F[_], T](implicit F: Sync[F]): F[H[T]] = F.delay(unsafe[T])
  @inline def createF[F[_], T](seed: T)(implicit F: Sync[F]): F[H[T]] = F.delay(unsafe[T](seed))

  @inline def unsafe[T]: H[T] = CreateSubject[H].replay[T]
  @inline def unsafe[T](seed: T): H[T] = CreateSubject[H].behavior[T](seed)

  object publish {
    // Create a Handler that just publish to all subscribers but does not keep the latest value as State, typically a PublishSubject

    @inline def create[T]: SyncIO[H[T]] = SyncIO(unsafe[T])
    @inline def createF[F[_], T](implicit F: Sync[F]): F[H[T]] = F.delay(unsafe[T])
    @inline def unsafe[T]: H[T] = CreateSubject[H].publish[T]
  }

  @inline final class CreatePartiallyApplied[F[_]] {
    @inline def apply[T](seed: T)(implicit F: Sync[F]): F[H[T]] = createF[F, T](seed)
  }
}

@inline final class ProHandlerFactory[H[_,_] : CreateProSubject] {
  // Create a ProHandler that has different type parameters for the Observer[I] part and the Observable[O] part

  @inline def apply[SI[_] : Sink, SO[_] : Source, I,O](sink: SI[I], source: SO[O]): H[I, O] = CreateProSubject[H].from(sink, source)
} 
Example 55
Source File: LocalStorage.scala    From outwatch   with Apache License 2.0 5 votes vote down vote up
package outwatch.util

import cats.effect.Sync
import cats.implicits._
import org.scalajs.dom
import org.scalajs.dom.StorageEvent
import org.scalajs.dom.window.{localStorage, sessionStorage}

import outwatch.dsl.events
import outwatch.reactive.handler._
import colibri._

class Storage(domStorage: dom.Storage) {
  private def handlerWithTransform[F[_]: Sync](key: String, transform: Observable[Option[String]] => Observable[Option[String]]): F[Handler[Option[String]]] = {
    val storage = new dom.ext.Storage(domStorage)

    for {
      h <- Handler.createF[F](storage(key))
    } yield {
      // We execute the write-action to the storage
      // and pass the written value through to the underlying subject h
      h.transformSubject[Option[String]] { o =>
        val c = o.redirect((o: Observable[Option[String]]) => transform(o).distinct)
        c.connect()
        c.sink
      } { input =>
        input.doOnNext {
          case Some(data) => storage.update(key, data)
          case None => storage.remove(key)
        }
      }
    }
  }

  private def storageEventsForKey[F[_]: Sync](key: String): Observable[Option[String]] =
    // StorageEvents are only fired if the localStorage was changed in another window
    events.window.onStorage.collect {
      case e: StorageEvent if e.storageArea == domStorage && e.key == key =>
        // newValue is either String or null if removed or cleared
        // Option() transformes this to Some(string) or None
        Option(e.newValue)
      case e: StorageEvent if e.storageArea == domStorage && e.key == null =>
        // storage.clear() emits an event with key == null
        None
    }

  def handlerWithoutEvents[F[_]: Sync](key: String): F[Handler[Option[String]]] = {
    handlerWithTransform(key, identity)
  }

  def handlerWithEventsOnly[F[_]: Sync](key: String): F[Handler[Option[String]]] = {
    val storageEvents = storageEventsForKey(key)
    handlerWithTransform(key, _ => storageEvents)
  }

  def handler[F[_]: Sync](key: String): F[Handler[Option[String]]] = {
    val storageEvents = storageEventsForKey(key)
    handlerWithTransform(key, Observable.merge(_, storageEvents))
  }
}

object LocalStorage extends Storage(localStorage)
object SessionStorage extends Storage(sessionStorage) 
Example 56
Source File: Store.scala    From outwatch   with Apache License 2.0 5 votes vote down vote up
package outwatch.util

import cats.effect.Sync

import colibri._

object Store {

  
  @inline final class CreatePartiallyApplied[F[_]] {
    @inline def apply[A, M](initialAction: A, initialState: M, reducer: Reducer[A, M])(implicit F: Sync[F]) = create[F, A, M](initialAction, initialState, reducer)
  }

  def create[F[_]] = new CreatePartiallyApplied[F]

  def create[F[_], A, M](
    initialAction: A,
    initialState: M,
    reducer: Reducer[A, M]
  )(implicit F: Sync[F]): F[ProSubject[A, (A, M)]] = F.delay {
    val subject = Subject.publish[A]

    val fold: ((A, M), A) => (A, M) = {
      case ((_, state), action) => {
        val (newState, effects) = reducer(state, action)

        effects.subscribe(Observer.create(subject.onNext))

        action -> newState
      }
    }

    subject.transformSubjectSource(source =>
      source
        .scan[(A, M)](initialAction -> initialState)(fold)
        .behavior(initialAction -> initialState).refCount
        .withDefaultSubscription[Observer](Observer.empty)
    )
  }
} 
Example 57
Source File: UnverifiedJWTSig.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.jws.signature

import java.time.Instant

import cats.effect.Sync
import tsec.jwt.JWTClaims
import tsec.jwt.algorithms.JWTSigAlgo
import tsec.signature.CryptoSignature
import tsec.signature.jca.{SigCertificate, SigErrorM, SigPublicKey}
import cats.syntax.flatMap._
import tsec.jws.JWSSerializer
import tsec.common._

final case class UnverifiedJWTSig[A: JWTSigAlgo](
    header: JWSSignedHeader[A],
    body: JWTClaims,
    signature: CryptoSignature[A]
) {
  def serialized(implicit hs: JWSSerializer[JWSSignedHeader[A]]): String =
    s"${hs.toB64URL(header)}.${JWTClaims.toB64URL(body)}.${signature.toB64UrlString}"
}

object UnverifiedJWTSig {
  def unverified[F[_], A: JWTSigAlgo](jwt: String)(implicit F: Sync[F], sigCV: JWSSigCV[F, A]): F[UnverifiedJWTSig[A]] =
    sigCV.extractRaw(jwt)

  def verifyK[F[_], A: JWTSigAlgo](
      jwt: UnverifiedJWTSig[A],
      pubKey: SigPublicKey[A]
  )(implicit F: Sync[F], sigCV: JWSSigCV[F, A], hs: JWSSerializer[JWSSignedHeader[A]]): F[JWTSig[A]] =
    F.delay(Instant.now()).flatMap(sigCV.verify(jwt.serialized, pubKey, _))

  def verifyC[F[_], A: JWTSigAlgo](
      jwt: UnverifiedJWTSig[A],
      cert: SigCertificate[A]
  )(implicit F: Sync[F], sigCV: JWSSigCV[F, A], hs: JWSSerializer[JWSSignedHeader[A]]): F[JWTSig[A]] =
    F.delay(Instant.now()).flatMap(sigCV.verifyCert(jwt.serialized, cert, _))
}

object UnverifiedJWTSigImpure {
  def unverified[A: JWTSigAlgo](jwt: String)(implicit sigCV: JWSSigCV[SigErrorM, A]): SigErrorM[UnverifiedJWTSig[A]] =
    sigCV.extractRaw(jwt)

  def verifyK[A: JWTSigAlgo](
      jwt: UnverifiedJWTSig[A],
      pubKey: SigPublicKey[A]
  )(implicit sigCV: JWSSigCV[SigErrorM, A], hs: JWSSerializer[JWSSignedHeader[A]]): SigErrorM[JWTSig[A]] =
    sigCV.verify(jwt.serialized, pubKey, Instant.now())

  def verifyC[A: JWTSigAlgo](
      jwt: UnverifiedJWTSig[A],
      cert: SigCertificate[A]
  )(implicit sigCV: JWSSigCV[SigErrorM, A], hs: JWSSerializer[JWSSignedHeader[A]]): SigErrorM[JWTSig[A]] =
    sigCV.verifyCert(jwt.serialized, cert, Instant.now())
} 
Example 58
Source File: StatefulJWTAuth.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authentication.internal

import java.time.Instant

import cats.data.OptionT
import cats.effect.Sync
import cats.syntax.all._
import org.http4s._
import tsec.authentication._
import tsec.common._
import tsec.jws.mac._
import tsec.jwt._
import tsec.jwt.algorithms.JWTMacAlgo
import tsec.mac.jca._

import scala.concurrent.duration.FiniteDuration


private[tsec] abstract class StatefulJWTAuth[F[_], I, V, A: JWTMacAlgo](
    val expiry: FiniteDuration,
    val maxIdle: Option[FiniteDuration],
    tokenStore: BackingStore[F, SecureRandomId, AugmentedJWT[A, I]],
    identityStore: IdentityStore[F, I, V],
    signingKey: MacSigningKey[A]
)(implicit F: Sync[F], cv: JWSMacCV[F, A])
    extends JWTAuthenticator[F, I, V, A] {

  private[tsec] def verifyAndRefresh(
      raw: String,
      retrieved: AugmentedJWT[A, I],
      now: Instant
  ): F[AugmentedJWT[A, I]]

  def parseRaw(raw: String, request: Request[F]): OptionT[F, SecuredRequest[F, V, AugmentedJWT[A, I]]] =
    OptionT(
      (for {
        now       <- F.delay(Instant.now())
        extracted <- cv.verifyAndParse(raw, signingKey, now)
        id        <- cataOption(extracted.id)
        retrieved <- tokenStore.get(SecureRandomId(id)).orAuthFailure
        refreshed <- verifyAndRefresh(raw, retrieved, now)
        identity  <- identityStore.get(retrieved.identity).orAuthFailure
      } yield SecuredRequest(request, identity, refreshed).some)
        .handleError(_ => None)
    )

  def create(body: I): F[AugmentedJWT[A, I]] =
    for {
      cookieId <- F.delay(SecureRandomId.Interactive.generate)
      now      <- F.delay(Instant.now())
      newExpiry = now.plusSeconds(expiry.toSeconds)
      claims = JWTClaims(
        issuedAt = Some(now),
        jwtId = Some(cookieId),
        expiration = Some(newExpiry)
      )
      signed  <- JWTMac.build[F, A](claims, signingKey)
      created <- tokenStore.put(AugmentedJWT(cookieId, signed, body, newExpiry, touch(now)))
    } yield created

  def renew(authenticator: AugmentedJWT[A, I]): F[AugmentedJWT[A, I]] =
    F.delay(Instant.now()).flatMap { now =>
      val updatedExpiry = now.plusSeconds(expiry.toSeconds)
      val newBody       = authenticator.jwt.body.withExpiry(updatedExpiry)
      for {
        reSigned <- JWTMac.build[F, A](newBody, signingKey)
        updated <- tokenStore
          .update(authenticator.copy(jwt = reSigned, expiry = updatedExpiry, lastTouched = touch(now)))
      } yield updated
    }

  def update(authenticator: AugmentedJWT[A, I]): F[AugmentedJWT[A, I]] =
    tokenStore.update(authenticator)

  def discard(authenticator: AugmentedJWT[A, I]): F[AugmentedJWT[A, I]] =
    tokenStore.delete(SecureRandomId.coerce(authenticator.id)).map(_ => authenticator)

  def afterBlock(response: Response[F], authenticator: AugmentedJWT[A, I]): OptionT[F, Response[F]] =
    OptionT.pure[F](response)
} 
Example 59
Source File: StatelessJWTAuth.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authentication.internal

import java.time.Instant

import cats.data.OptionT
import cats.effect.Sync
import cats.syntax.all._
import io.circe.syntax._
import io.circe.{Decoder, Encoder}
import org.http4s._
import tsec.authentication._
import tsec.common._
import tsec.jws.mac._
import tsec.jwt.algorithms.JWTMacAlgo
import tsec.jwt.JWTClaims
import tsec.mac.MAC
import tsec.mac.jca._

import scala.concurrent.duration._

private[tsec] abstract class StatelessJWTAuth[F[_], V: Decoder: Encoder.AsObject, A: JWTMacAlgo](
    val expiry: FiniteDuration,
    val maxIdle: Option[FiniteDuration],
    signingKey: MacSigningKey[A]
)(implicit F: Sync[F], cv: JWSMacCV[F, A])
    extends JWTAuthenticator[F, V, V, A] {

  private[tsec] def verifyLastTouched(body: JWTMac[A], now: Instant): F[Option[Instant]]

  def parseRaw(raw: String, request: Request[F]): OptionT[F, SecuredRequest[F, V, AugmentedJWT[A, V]]] =
    OptionT(
      (for {
        now         <- F.delay(Instant.now())
        extracted   <- cv.verifyAndParse(raw, signingKey, now)
        jwtid       <- cataOption(extracted.id)
        body        <- extracted.body.asF[F, V]
        expiry      <- cataOption(extracted.body.expiration)
        lastTouched <- verifyLastTouched(extracted, now)
        augmented = AugmentedJWT(
          SecureRandomId.coerce(jwtid),
          extracted,
          body,
          expiry,
          lastTouched
        )
        refreshed <- refresh(augmented)
      } yield SecuredRequest(request, body, refreshed).some)
        .handleError(_ => None)
    )

  def create(body: V): F[AugmentedJWT[A, V]] =
    for {
      now   <- F.delay(Instant.now())
      jwtId <- SecureRandomId.Interactive.generateF[F]
      expiryTime  = now.plusSeconds(expiry.toSeconds)
      lastTouched = touch(now)
      claims = JWTClaims(
        issuedAt = touch(now),
        jwtId = Some(jwtId),
        expiration = Some(expiryTime),
        customFields = body.asJsonObject.toList
      )
      out <- JWTMac.build[F, A](claims, signingKey)
    } yield AugmentedJWT(jwtId, out, body, expiryTime, lastTouched)

  def update(authenticator: AugmentedJWT[A, V]): F[AugmentedJWT[A, V]] =
    F.pure(authenticator)

  def renew(authenticator: AugmentedJWT[A, V]): F[AugmentedJWT[A, V]] =
    for {
      now <- F.delay(Instant.now())
      updatedExpiry = now.plusSeconds(expiry.toSeconds)
      authBody      = authenticator.jwt.body
      lastTouched   = touch(now)
      jwt <- JWTMac.build(
        authBody.withIATOption(lastTouched).withExpiry(updatedExpiry),
        signingKey
      )
    } yield AugmentedJWT(authenticator.id, jwt, authenticator.identity, updatedExpiry, lastTouched)

  def discard(authenticator: AugmentedJWT[A, V]): F[AugmentedJWT[A, V]] =
    F.pure(authenticator.copy(jwt = JWTMac.buildToken[A](JWSMacHeader[A], JWTClaims(), MAC[A](Array.empty[Byte]))))

  def afterBlock(response: Response[F], authenticator: AugmentedJWT[A, V]): OptionT[F, Response[F]] =
    OptionT.pure[F](embed(response, authenticator))
} 
Example 60
Source File: PartialStatelessJWTAuth.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authentication.internal

import java.time.Instant

import cats.data.OptionT
import cats.effect.Sync
import cats.syntax.all._
import io.circe.parser.decode
import io.circe.syntax._
import io.circe.{Decoder, Encoder}
import org.http4s._
import tsec.authentication._
import tsec.common._
import tsec.jws.mac._
import tsec.jwt.algorithms.JWTMacAlgo
import tsec.jwt.{JWTClaims, JWTPrinter}
import tsec.mac.jca._

import scala.concurrent.duration._


  def discard(authenticator: AugmentedJWT[A, I]): F[AugmentedJWT[A, I]] =
    for {
      now <- F.delay(Instant.now)
      jwt <- JWTMac
        .build[F, A](
          authenticator.jwt.body
            .withExpiry(now)
            .withJwtID(SecureRandomId.Interactive.generate),
          signingKey
        )
    } yield AugmentedJWT(authenticator.id, jwt, authenticator.identity, now, authenticator.lastTouched)
} 
Example 61
Source File: CredentialStore.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.authentication.credentials

import cats.data.OptionT
import cats.effect.Sync
import cats.syntax.all._
import tsec.passwordhashers._
import tsec.passwordhashers.jca._


  def updateCredentials(credentials: C, update: P => F[Unit]): F[Unit] =
    putCredentials(credentials, update)

  def isAuthenticated(credentials: C): F[Boolean]
}

abstract class PasswordStore[F[_], Id, P](implicit P: PasswordHasher[F, P], F: Sync[F])
    extends CredentialStore[F, RawCredentials[Id], PasswordHash[P]] {

  def retrievePass(id: Id): OptionT[F, PasswordHash[P]]

  def putCredentials(credentials: RawCredentials[Id], put: PasswordHash[P] => F[Unit]): F[Unit] =
    for {
      hash <- P.hashpw(credentials.rawPassword)
      _    <- put(hash)
    } yield ()

  def putCredentials(raw: Array[Byte], put: PasswordHash[P] => F[Unit]): F[Unit] =
    for {
      hash <- P.hashpw(raw)
      _    <- put(hash)
    } yield ()

  def putCredentials(raw: Array[Char], put: PasswordHash[P] => F[Unit]): F[Unit] =
    for {
      hash <- P.hashpw(raw)
      _    <- put(hash)
    } yield ()

  def isAuthenticated(credentials: RawCredentials[Id]): F[Boolean] =
    for {
      pass <- retrievePass(credentials.identity)
        .getOrElseF(F.raiseError(CredentialsError("No such user")))
      check <- P.checkpwBool(credentials.rawPassword, pass)
    } yield check

  def isAuthenticated(id: Id, raw: Array[Byte]): F[Boolean] =
    for {
      pass <- retrievePass(id)
        .getOrElseF(F.raiseError(CredentialsError("No such user")))
      check <- P.checkpwBool(raw, pass)
    } yield check

  def isAuthenticated(id: Id, raw: Array[Char]): F[Boolean] =
    for {
      pass <- retrievePass(id)
        .getOrElseF(F.raiseError(CredentialsError("No such user")))
      check <- P.checkpwBool(raw, pass)
    } yield check
}

trait SCryptPasswordStore[F[_], Id] extends PasswordStore[F, Id, SCrypt]

trait BCryptPasswordStore[F[_], Id] extends PasswordStore[F, Id, BCrypt] 
Example 62
Source File: AEADCookieEncryptor.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.cookies

import cats.effect.Sync
import cats.syntax.all._
import tsec.cipher.common.padding.NoPadding
import tsec.cipher.symmetric._
import tsec.cipher.symmetric.jca._
import tsec.common._

object AEADCookieEncryptor {

  def signAndEncrypt[F[_], A](message: String, aad: AAD, key: SecretKey[A])(
      implicit authEncryptor: JAuthEncryptor[F, A],
      ivStrat: IvGen[F, A],
      F: Sync[F]
  ): F[AEADCookie[A]] =
    if (message.isEmpty)
      F.raiseError(EncryptError("Cannot encrypt an empty string!"))
    else {
      val messageBytes = message.utf8Bytes
      for {
        iv        <- ivStrat.genIv
        encrypted <- authEncryptor.encryptWithAAD(PlainText(messageBytes), key, iv, aad)
      } yield AEADCookie.fromEncrypted[A](encrypted, aad)
    }

  def retrieveFromSigned[F[_], A: AES](message: AEADCookie[A], key: SecretKey[A])(
      implicit authEncryptor: JAuthEncryptor[F, A],
      F: Sync[F],
      ivStrat: IvGen[F, A]
  ): F[String] = {
    val split = message.split("-")
    if (split.length != 2)
      F.raiseError(DecryptError("Could not decode cookie"))
    else {
      for {
        aad <- split(1).b64Bytes
          .fold[F[AAD]](F.raiseError(DecryptError("Could not decode cookie")))(arr => F.pure(AAD(arr)))
        rawCTBytes <- split(0).b64Bytes
          .fold[F[Array[Byte]]](F.raiseError(DecryptError("Could not decode cookie")))(F.pure)
        cipherText <- F.fromEither(CTOPS.ciphertextFromArray[A, GCM, NoPadding](rawCTBytes))
        decrypted  <- authEncryptor.decryptWithAAD(cipherText, key, aad)
      } yield decrypted.toUtf8String
    }
  }

} 
Example 63
Source File: JWTSignatureExamples.scala    From tsec   with MIT License 5 votes vote down vote up
object JWTSignatureExamples {

  import cats.effect.Sync
  import cats.syntax.all._
  import tsec.jws.signature._
  import tsec.jwt._
  import tsec.signature.jca._

  
  val claims = JWTClaims()

  def jwtStuffMonadic[F[_]](implicit F: Sync[F]): F[JWTSig[SHA256withECDSA]] = for {
    keyPair      <- SHA256withECDSA.generateKeyPair[F]
    jwtSig       <- JWTSig.signAndBuild[F, SHA256withECDSA](claims, keyPair.privateKey) //ToInstance
    jwtSigString <- JWTSig.signToString[F, SHA256withECDSA](claims, keyPair.privateKey)
    verified1    <- JWTSig.verifyK[F, SHA256withECDSA](jwtSigString, keyPair.publicKey)
    verified2    <- JWTSig.verifyKI[F, SHA256withECDSA](jwtSig, keyPair.publicKey)
  } yield verified2

  val jwtStuff: Either[Throwable, JWTSig[SHA256withECDSA]] = for {
    keyPair      <- SHA256withECDSA.generateKeyPair[SigErrorM]
    jwtSig       <- JWTSigImpure.signAndBuild[SHA256withECDSA](claims, keyPair.privateKey) //ToInstance
    jwtSigString <- JWTSigImpure.signToString(claims, keyPair.privateKey)
    verified1    <- JWTSigImpure.verifyK(jwtSigString, keyPair.publicKey)
    verified2    <- JWTSigImpure.verifyKI(jwtSig, keyPair.publicKey)
  } yield verified2

} 
Example 64
Source File: JWTMacExamples.scala    From tsec   with MIT License 5 votes vote down vote up
import java.time.Instant

object JWTMacExamples {

  import cats.effect.Sync
  import cats.syntax.all._
  import tsec.jws.mac._
  import tsec.jwt._
  import tsec.mac.jca._

  import scala.concurrent.duration._

  
  val impureClaims = JWTClaims(expiration = Some(Instant.now.plusSeconds(10.minutes.toSeconds)))

  val jwt: Either[Throwable, JWTMac[HMACSHA256]] = for {
    key             <- HMACSHA256.generateKey[MacErrorM]
    jwt             <- JWTMacImpure.build[HMACSHA256](impureClaims, key) //You can sign and build a jwt object directly
    verifiedFromObj <- JWTMacImpure.verifyFromInstance[HMACSHA256](jwt, key)
    stringjwt       <- JWTMacImpure.buildToString[HMACSHA256](impureClaims, key) //Or build it straight to string
    isverified      <- JWTMacImpure.verifyFromString[HMACSHA256](stringjwt, key) //You can verify straight from a string
    parsed          <- JWTMacImpure.verifyAndParse[HMACSHA256](stringjwt, key) //Or verify and return the actual instance
  } yield parsed

} 
Example 65
Source File: BCrypt.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.passwordhashers.jca

import java.nio.CharBuffer

import cats.effect.Sync
import tsec.common._
import tsec.passwordhashers._
import tsec.passwordhashers.jca.internal.JBCrypt

sealed trait BCrypt

object BCrypt extends JCAPasswordPlatform[BCrypt] {

  private[tsec] def unsafeHashpw(p: Array[Byte]): String =
    JBCrypt.hashpw(p, JBCryptUtil.genSalt(DefaultBcryptRounds))

  private[tsec] def unsafeCheckpw(p: Array[Byte], hash: PasswordHash[BCrypt]): Boolean =
    JBCrypt.checkpw(p, hash)

  def hashpwWithRounds[F[_]](p: String, rounds: Int)(implicit F: Sync[F]): F[PasswordHash[BCrypt]] =
    hashpwWithRounds[F](p.asciiBytes, rounds)

  def hashpwWithRounds[F[_]](p: Array[Byte], rounds: Int)(implicit F: Sync[F]): F[PasswordHash[BCrypt]] =
    if (rounds < 10 || rounds > 30)
      F.raiseError(PasswordError("Invalid number of rounds"))
    else
      F.delay {
        val out = PasswordHash[BCrypt](JBCrypt.hashpw(p, JBCryptUtil.genSalt(rounds)))
        ByteUtils.zeroByteArray(p)
        out
      }

  def hashpwWithRounds[F[_]](p: Array[Char], rounds: Int)(implicit F: Sync[F]): F[PasswordHash[BCrypt]] =
    if (rounds < 10 || rounds > 30)
      F.raiseError(PasswordError("Invalid number of rounds"))
    else
      F.delay {
        val charbuffer = CharBuffer.wrap(p)
        val bytes      = defaultCharset.encode(charbuffer).array()
        val out        = PasswordHash[BCrypt](JBCrypt.hashpw(bytes, JBCryptUtil.genSalt(rounds)))
        //Clear pass
        ByteUtils.zeroCharArray(p)
        ByteUtils.zeroByteArray(bytes)
        out
      }
} 
Example 66
Source File: WithMacSigningKey.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.mac.jca

import javax.crypto.{KeyGenerator, Mac, SecretKey}
import javax.crypto.spec.SecretKeySpec
import cats.Id
import cats.effect.Sync
import cats.instances.either._
import cats.syntax.either._
import tsec.keygen.symmetric.{IdKeyGen, SymmetricKeyGen}
import tsec.mac.{MAC, MacAPI}

protected[tsec] abstract class WithMacSigningKey[A](algo: String, keyLenBits: Int)
    extends MacKeyGenerator[A]
    with MacAPI[A, MacSigningKey] {

  implicit def syncMac[F[_]](implicit F: Sync[F]): JCAMessageAuth[F, A] =
    new JCAMessageAuth[F, A]() {

      def algorithm: String = algo

      protected[tsec] def genInstance: F[Mac] = F.delay(Mac.getInstance(algo))

      protected[tsec] def signInternal(m: Mac, k: SecretKey, content: Array[Byte]): F[MAC[A]] = F.delay {
        m.init(k)
        MAC[A](m.doFinal(content))
      }
    }

  implicit val macInstanceEither: JCAMessageAuth[MacErrorM, A] =
    new JCAMessageAuth[MacErrorM, A]() {

      def algorithm: String = algo

      protected[tsec] def genInstance: MacErrorM[Mac] = Either.catchNonFatal(Mac.getInstance(algo))

      protected[tsec] def signInternal(m: Mac, k: SecretKey, content: Array[Byte]): MacErrorM[MAC[A]] =
        Either.catchNonFatal {
          m.init(k)
          MAC[A](m.doFinal(content))
        }
    }

  implicit def genKeyMac[F[_]](implicit F: Sync[F]): MacKeyGen[F, A] =
    new SymmetricKeyGen[F, A, MacSigningKey] {
      def generateKey: F[MacSigningKey[A]] =
        F.delay(impl.generateKeyUnsafe())

      def build(rawKey: Array[Byte]): F[MacSigningKey[A]] =
        F.delay(MacSigningKey[A](new SecretKeySpec(rawKey, algo)))
    }

  implicit def genKeyMacError: MacKeyGen[MacErrorM, A] = new MacKeyGen[MacErrorM, A] {
    def generateKey: MacErrorM[MacSigningKey[A]] =
      Either.catchNonFatal(impl.generateKeyUnsafe())

    def build(rawKey: Array[Byte]): MacErrorM[MacSigningKey[A]] =
      Either.catchNonFatal(impl.buildKeyUnsafe(rawKey))
  }

  implicit val idKeygenMac: IdKeyGen[A, MacSigningKey] =
    new IdKeyGen[A, MacSigningKey] {
      def generateKey: Id[MacSigningKey[A]] =
        impl.generateKeyUnsafe()

      def build(rawKey: Array[Byte]): Id[MacSigningKey[A]] =
        impl.buildKeyUnsafe(rawKey)
    }

  object impl {
    def generator: KeyGenerator = KeyGenerator.getInstance(algo)

    def generateKeyUnsafe(): MacSigningKey[A] = MacSigningKey.fromJavaKey[A](generator.generateKey())

    def buildKeyUnsafe(key: Array[Byte]): MacSigningKey[A] =
      MacSigningKey.fromJavaKey[A](new SecretKeySpec(key, algo))
  }

  implicit def keyGen: MacKeyGenerator[A] = this
} 
Example 67
Source File: SecureRandomIdGenerator.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.common

import cats.effect.Sync
import org.apache.commons.codec.binary.Hex

case class SecureRandomIdGenerator(sizeInBytes: Int = 32) extends ManagedRandom {
  def generate: SecureRandomId = {
    val byteArray = new Array[Byte](sizeInBytes)
    nextBytes(byteArray)
    new String(Hex.encodeHex(byteArray)).asInstanceOf[SecureRandomId]
  }

  def generateF[F[_]](implicit F: Sync[F]): F[SecureRandomId] = F.delay(generate)
}

//Todo: Possible use case for refined?
object SecureRandomId {
  lazy val Strong: SecureRandomIdGenerator      = SecureRandomIdGenerator(32)
  lazy val Interactive: SecureRandomIdGenerator = SecureRandomIdGenerator(16)

  def apply(s: String): SecureRandomId  = s.asInstanceOf[SecureRandomId]
  def coerce(s: String): SecureRandomId = s.asInstanceOf[SecureRandomId]

} 
Example 68
Source File: KeyDerivation.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.kdf.libsodium

import cats.effect.Sync
import tsec.common._
import tsec.libsodium.ScalaSodium

sealed trait KeyDerivation

object KeyDerivation {

  def generateKey[F[_]](implicit F: Sync[F], S: ScalaSodium): F[MasterKey] = F.delay {
    val masterKey = MasterKey(new Array[Byte](ScalaSodium.crypto_kdf_KEYBYTES))
    S.crypto_kdf_keygen(masterKey)
    masterKey
  }

  def deriveKey[F[_]](
      masterKey: MasterKey,
      keyLength: Int,
      id: Int,
      context: String
  )(implicit F: Sync[F], S: ScalaSodium): F[DerivedKey] = F.delay {
    if (ScalaSodium.crypto_kdf_BYTES_MIN > keyLength || keyLength > ScalaSodium.crypto_kdf_BYTES_MAX)
      throw KeyLengthError(keyLength)

    val ctx = context.utf8Bytes
    if (ctx.length != ScalaSodium.crypto_kdf_CONTEXTBYTES)
      throw ContextBytesError(ctx.length)

    val subKey = DerivedKey(new Array[Byte](keyLength))
    S.crypto_kdf_derive_from_key(subKey, keyLength, id, ctx, masterKey)

    subKey
  }
} 
Example 69
Source File: SodiumHashPlatform.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.hashing.libsodium.internal

import cats.Id
import cats.effect.Sync
import fs2._
import tsec.hashing.libsodium._
import tsec.libsodium.ScalaSodium
import tsec.hashing._

abstract class SodiumHashPlatform[A](val algorithm: String) extends SodiumHash[A] with SodiumHashAPI[A] { self =>
  implicit val sodiumHash: SodiumHash[A]           = this
  implicit val sodiumHashAlgebra: SodiumHashAPI[A] = this

  implicit def hasher[F[_]](implicit F: Sync[F], S: ScalaSodium): CryptoHasher[F, A] =
    new CryptoHasher[F, A] {
      def algorithm: String = self.algorithm

      def hash(bytes: Array[Byte]): F[CryptoHash[A]] = F.delay(impl.unsafeHash(bytes))

      def hashPipe: Pipe[F, Byte, Byte] = impl.hashPipe[F]
    }

  implicit def idHasher(implicit S: ScalaSodium): CryptoHasher[Id, A] =
    new CryptoHasher[Id, A] {

      def algorithm: String = self.algorithm

      def hash(bytes: Array[Byte]): Id[CryptoHash[A]] = impl.unsafeHash(bytes)

      
      def hashPipe: Pipe[Id, Byte, Byte] =
        in =>
          Stream.suspend[Id, Byte] {
            for {
              rawState <- Stream.suspend(Stream.emit {
                val state = HashState[A](new Array[Byte](stateSize))
                sodiumHashInit(state)
                state
              })
              ast <- in.chunks.fold(rawState) { (st, in) =>
                sodiumHashChunk(st, in.toBytes.toArray)
                st
              }
              out <- Stream.suspend(Stream.emit {
                val out = new Array[Byte](hashLen)
                sodiumHashFinal(ast, out)
                out
              })
              c <- Stream.chunk(Chunk.bytes(out)).covary[Id]
            } yield c
        }

    }

  object impl {
    def unsafeHash(bytes: Array[Byte])(implicit S: ScalaSodium): CryptoHash[A] = {
      val out = new Array[Byte](hashLen)
      sodiumHash(bytes, out)
      CryptoHash[A](out)
    }

    final def hashPipe[F[_]](implicit F: Sync[F], S: ScalaSodium): Pipe[F, Byte, Byte] = { in =>
      Stream.suspend[F, Byte] {
        for {
          rawState <- Stream.eval(F.delay {
            val state = HashState[A](new Array[Byte](stateSize))
            sodiumHashInit(state)
            state
          })
          ast <- in.chunks.fold(rawState) { (st, in) =>
            sodiumHashChunk(st, in.toBytes.toArray)
            st
          }
          out <- Stream.eval(F.delay {
            val out = new Array[Byte](hashLen)
            sodiumHashFinal(ast, out)
            out
          })
          c <- Stream.chunk(Chunk.bytes(out)).covary[F]
        } yield c
      }
    }
  }
} 
Example 70
Source File: Blake2b.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.hashing.libsodium

import java.security.MessageDigest

import cats.effect.Sync
import tsec.hashing._
import tsec.hashing.libsodium.internal.SodiumHashPlatform
import tsec.libsodium.ScalaSodium
import tsec.libsodium.ScalaSodium.NullPtrBytes

sealed trait Blake2b

object Blake2b extends SodiumHashPlatform[Blake2b]("Blake2b") {
  val MinKeyLen     = ScalaSodium.crypto_generichash_blake2b_KEYBYTES_MIN
  val DefaultKeyLen = ScalaSodium.crypto_generichash_blake2b_KEYBYTES
  val MaxKeyLen     = ScalaSodium.crypto_generichash_blake2b_KEYBYTES_MAX

  val MinHashLen = ScalaSodium.crypto_generichash_blake2b_BYTES_MIN
  val MaxHashLen = ScalaSodium.crypto_generichash_blake2b_BYTES_MAX

  
  val hashLen: Int = ScalaSodium.crypto_generichash_blake2b_BYTES

  def generateKey[F[_]](implicit F: Sync[F], S: ScalaSodium): F[BlakeKey] = F.delay {
    BlakeKey(ScalaSodium.randomBytesUnsafe(DefaultKeyLen))
  }

  def generateMinKey[F[_]](implicit F: Sync[F], S: ScalaSodium): F[BlakeKey] = F.delay {
    BlakeKey(ScalaSodium.randomBytesUnsafe(MinKeyLen))
  }

  def generateMaxKey[F[_]](implicit F: Sync[F], S: ScalaSodium): F[BlakeKey] = F.delay {
    BlakeKey(ScalaSodium.randomBytesUnsafe(MaxKeyLen))
  }

  def generateKeyVarLen[F[_]](len: Int)(implicit F: Sync[F], S: ScalaSodium): F[BlakeKey] = F.delay {
    val outLen = math.max(MinKeyLen, math.min(MaxKeyLen, len))
    BlakeKey(ScalaSodium.randomBytesUnsafe(outLen))
  }

  def hashVarLen[F[_]](
      in: Array[Byte],
      len: Int = hashLen
  )(implicit F: Sync[F], S: ScalaSodium): F[CryptoHash[Blake2b]] =
    F.delay {
      val outLen = math.max(MinHashLen, math.min(MaxHashLen, len))
      val out    = new Array[Byte](outLen)
      S.crypto_generichash(out, outLen, in, in.length, NullPtrBytes, 0)
      CryptoHash[Blake2b](out)
    }

  def verify[F[_]](in: Array[Byte], compare: CryptoHash[Blake2b], key: BlakeKey)(
      implicit F: Sync[F],
      S: ScalaSodium
  ): F[Boolean] =
    F.delay {
      val out = new Array[Byte](compare.length)
      S.crypto_generichash(out, compare.length, in, in.length, key, key.length)
      MessageDigest.isEqual(out, compare)
    }

  def hashKeyed[F[_]](in: Array[Byte], key: BlakeKey)(implicit F: Sync[F], S: ScalaSodium): F[CryptoHash[Blake2b]] =
    F.delay {
      val out = new Array[Byte](hashLen)
      S.crypto_generichash(out, hashLen, in, in.length, key, key.length)
      CryptoHash[Blake2b](out)
    }

  def hashKeyedVarLen[F[_]](in: Array[Byte], key: BlakeKey, len: Int = hashLen)(
      implicit F: Sync[F],
      S: ScalaSodium
  ): F[CryptoHash[Blake2b]] = F.delay {
    val outLen = math.max(MinHashLen, math.min(MaxHashLen, len))
    val out    = new Array[Byte](outLen)
    S.crypto_generichash(out, outLen, in, in.length, key, key.length)
    CryptoHash[Blake2b](out)
  }

  def stateSize(implicit S: ScalaSodium): Int = S.crypto_generichash_statebytes

  def sodiumHash(in: Array[Byte], out: Array[Byte])(implicit S: ScalaSodium): Int =
    S.crypto_generichash(out, hashLen, in, in.length, NullPtrBytes, 0)

  def sodiumHashInit(state: HashState[Blake2b])(implicit S: ScalaSodium): Int =
    S.crypto_generichash_blake2b_init(state, NullPtrBytes, 0, hashLen)

  def sodiumHashChunk(state: HashState[Blake2b], in: Array[Byte])(implicit S: ScalaSodium): Int =
    S.crypto_generichash_update(state, in, in.length)

  def sodiumHashFinal(state: HashState[Blake2b], out: Array[Byte])(implicit S: ScalaSodium): Int =
    S.crypto_generichash_final(state, out, hashLen)

} 
Example 71
Source File: ScalaSodium.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.libsodium

import cats.effect.Sync
import org.log4s._
import tsec.internal._
import tsec.jni._


  private def checkVersion(sodiumString: String): Unit = {
    val error = new SodiumLoadError(s"Unsupported libsodium version $sodiumString. Please upgrade to version 1.0.12+")

    sodiumString.trim.split("\\.") match {
      case Array(major, med, minor) =>
        if (major.toInt < 1 || (minor.toInt < 12 && med.toInt == 0)) {
          logger.error(error)("Unsupported version")
          throw error
        } else
          logger.info("Loading libsodium jni... Hold tight Asznee")
      case _ =>
        logger.error(error)("Unsupported version")
        throw error
    }
  }

  private[tsec] lazy val Sodium: ScalaSodium = {
    System.loadLibrary(libraryName)
    val sodium = new ScalaSodium()
    checkVersion(SodiumJNI.sodium_version_string.asInstanceOf[String])
    if (sodium.sodium_init < 0) {
      val err = new SodiumLoadError("Has not been initialized properly")
      logger.error(err)("ScalaSodium has not been initialized properly")
      throw err
    }
    logger.info("Libsodium loaded properly")

    sodium
  }

  final def getSodiumUnsafe: ScalaSodium = Sodium

  final def getSodium[F[_]](implicit F: Sync[F]): F[ScalaSodium] = F.delay(Sodium)

  final def randomBytes[F[_]](len: Int)(implicit F: Sync[F], S: ScalaSodium): F[Array[Byte]] = F.delay {
    val bytes = new Array[Byte](len)
    S.randombytes_buf(bytes, len)
    bytes
  }

  final def randomBytesUnsafe(len: Int)(implicit S: ScalaSodium): Array[Byte] = {
    val bytes = new Array[Byte](len)
    S.randombytes_buf(bytes, len)
    bytes
  }

  private final class SodiumLoadError(reason: String) extends VirtualMachineError {
    override def getMessage: String = reason
  }

} 
Example 72
Source File: Argon2.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.passwordhashers.libsodium

import cats.effect.Sync
import tsec.common._
import tsec.libsodium.ScalaSodium
import tsec.passwordhashers.libsodium.internal.SodiumPasswordHasher
import tsec.passwordhashers.{PasswordHash, PasswordHasher}

sealed trait Argon2

object Argon2 extends SodiumPasswordHasher[Argon2] {
  implicit val hasher: SodiumPasswordHasher[Argon2] = this
  val hashingAlgorithm: String                      = "Argon2id"
  val saltLen: Int                                  = ScalaSodium.crypto_pwhash_argon2id_SALTBYTES
  val outLen: Int                                   = ScalaSodium.crypto_pwhash_argon2id_STRBYTES

  implicit def passwordHasher[F[_]](implicit F: Sync[F], S: ScalaSodium): PasswordHasher[F, Argon2] =
    new PasswordHasher[F, Argon2] {
      def hashpw(p: Array[Char]): F[PasswordHash[Argon2]] =
        F.delay(hashpwUnsafe(p))

      def hashpw(p: Array[Byte]): F[PasswordHash[Argon2]] =
        F.delay(hashpwUnsafe(p))

      def checkpwBool(p: Array[Char], hash: PasswordHash[Argon2]): F[Boolean] =
        F.delay(checkpwUnsafe(p, hash))

      def checkpwBool(p: Array[Byte], hash: PasswordHash[Argon2]): F[Boolean] =
        F.delay(checkpwUnsafe(p, hash))

      private[tsec] def hashPassUnsafe(p: Array[Byte]): String =
        impl.unsafeHashpw(p, PasswordStrength.InteractiveStrength)

      private[tsec] def checkPassUnsafe(p: Array[Byte], hash: PasswordHash[Argon2]): Boolean =
        impl.unsafeCheckpw(p, hash)
    }

  def hashpwWithStrength[F[_], S](
      p: String,
      strength: S
  )(implicit pws: PWStrengthParam[Argon2, S], F: Sync[F], S: ScalaSodium): F[PasswordHash[Argon2]] =
    F.delay(impl.unsafeHashpw(p.asciiBytes, strength))

  def checkPass[F[_]](raw: String, hash: PasswordHash[Argon2])(implicit F: Sync[F], S: ScalaSodium): F[Boolean] =
    F.delay(impl.unsafeCheckpw(raw.asciiBytes, hash))

  object impl {
    def unsafeHashpw[S](
        passBytes: Array[Byte],
        strength: S
    )(implicit pws: PWStrengthParam[Argon2, S], S: ScalaSodium): PasswordHash[Argon2] = {
      val out = new Array[Byte](outLen)
      if (passBytes.isEmpty)
        throw SodiumPasswordError("Incorrect format")
      else if (S.crypto_pwhash_str(out, passBytes, passBytes.length, pws.opLimit, pws.memLimit) != 0)
        throw SodiumPasswordError("Could not hash password. Possibly out of memory")
      else
        PasswordHash[Argon2](out.toAsciiString)
    }

    def unsafeCheckpw(raw: Array[Byte], hash: PasswordHash[Argon2])(implicit S: ScalaSodium): Boolean =
      S.crypto_pwhash_str_verify(hash.asciiBytes, raw, raw.length) == 0
  }
} 
Example 73
Source File: SodiumPasswordHasher.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.passwordhashers.libsodium.internal

import cats.effect.Sync
import cats.syntax.all._
import tsec.libsodium.ScalaSodium
import tsec.passwordhashers._
import tsec.passwordhashers.libsodium._

trait SodiumPasswordHasher[P] extends PasswordHashAPI[P] {
  val hashingAlgorithm: String
  val saltLen: Int
  val outLen: Int

  def hashpwWithStrength[F[_], S](
      p: String,
      strength: S
  )(implicit pws: PWStrengthParam[P, S], F: Sync[F], S: ScalaSodium): F[PasswordHash[P]]

  final def checkPassShortCircuit[F[_]](
      raw: String,
      hash: PasswordHash[P]
  )(implicit F: Sync[F], S: ScalaSodium, P: PasswordHasher[F, P]): F[Unit] =
    checkpwBool[F](raw, hash).flatMap(res => if (res) F.unit else F.raiseError(SodiumPasswordError("Invalid password")))

} 
Example 74
Source File: SodiumSCrypt.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.passwordhashers.libsodium

import cats.effect.Sync
import tsec.common._
import tsec.libsodium.ScalaSodium
import tsec.passwordhashers.libsodium.internal.SodiumPasswordHasher
import tsec.passwordhashers.{PasswordHasher, _}

sealed trait SodiumSCrypt

object SodiumSCrypt extends SodiumPasswordHasher[SodiumSCrypt] {
  implicit val hasher: SodiumPasswordHasher[SodiumSCrypt] = this

  val hashingAlgorithm: String = "SCrypt"
  val saltLen: Int             = ScalaSodium.crypto_pwhash_scryptsalsa208sha256_SALTBYTES
  val outLen: Int              = ScalaSodium.crypto_pwhash_scryptsalsa208sha256_STRBYTES

  def hashpwWithStrength[F[_], S](
      p: String,
      strength: S
  )(implicit pws: PWStrengthParam[SodiumSCrypt, S], F: Sync[F], S: ScalaSodium): F[PasswordHash[SodiumSCrypt]] =
    F.delay(impl.unsafeHashpw(p.asciiBytes, strength))

  implicit def genHasher[F[_]](implicit F: Sync[F], S: ScalaSodium): PasswordHasher[F, SodiumSCrypt] =
    new PasswordHasher[F, SodiumSCrypt] {
      def hashpw(p: Array[Char]): F[PasswordHash[SodiumSCrypt]] =
        F.delay(hashpwUnsafe(p))

      def hashpw(p: Array[Byte]): F[PasswordHash[SodiumSCrypt]] =
        F.delay(hashpwUnsafe(p))

      def checkpwBool(p: Array[Char], hash: PasswordHash[SodiumSCrypt]): F[Boolean] =
        F.delay(checkpwUnsafe(p, hash))

      def checkpwBool(p: Array[Byte], hash: PasswordHash[SodiumSCrypt]): F[Boolean] =
        F.delay(checkpwUnsafe(p, hash))

      private[tsec] def hashPassUnsafe(p: Array[Byte]): String =
        impl.unsafeHashpw(p, PasswordStrength.InteractiveStrength)

      private[tsec] def checkPassUnsafe(p: Array[Byte], hash: PasswordHash[SodiumSCrypt]): Boolean =
        impl.unsafeCheckpw(p, hash)
    }

  object impl {
    def unsafeHashpw[S](
        passBytes: Array[Byte],
        strength: S
    )(implicit pws: PWStrengthParam[SodiumSCrypt, S], S: ScalaSodium): PasswordHash[SodiumSCrypt] = {
      val out = new Array[Byte](outLen)
      if (passBytes.isEmpty)
        throw SodiumPasswordError("Incorrect format")
      else if (S.crypto_pwhash_scryptsalsa208sha256_str(out, passBytes, passBytes.length, pws.opLimit, pws.memLimit) != 0)
        throw SodiumPasswordError("Could not hash password. Possibly out of memory")
      else
        PasswordHash[SodiumSCrypt](out.toAsciiString)
    }

    def unsafeCheckpw(rawBytes: Array[Byte], hash: PasswordHash[SodiumSCrypt])(
        implicit S: ScalaSodium
    ): Boolean =
      S.crypto_pwhash_scryptsalsa208sha256_str_verify(hash.asciiBytes, rawBytes, rawBytes.length) == 0
  }
} 
Example 75
Source File: SodiumMacPlatform.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.mac.libsodium

import cats.effect.Sync
import tsec.keygen.symmetric.SymmetricKeyGen
import tsec.libsodium.ScalaSodium
import tsec.mac.{MessageAuth, _}

private[tsec] abstract class SodiumMacPlatform[A](algo: String) extends SodiumMacAlgo[A] with SodiumMacAPI[A] {
  implicit val sm: SodiumMacAlgo[A]        = this
  implicit val macAlgebra: SodiumMacAPI[A] = this

  implicit def symmGen[F[_]](implicit F: Sync[F], S: ScalaSodium): SymmetricKeyGen[F, A, SodiumMACKey] =
    new SymmetricKeyGen[F, A, SodiumMACKey] {
      def generateKey: F[SodiumMACKey[A]] = F.delay(impl.unsafeGenerateKey)

      def build(rawKey: Array[Byte]): F[SodiumMACKey[A]] =
        F.delay(impl.unsafeBuildKey(rawKey))
    }

  implicit def authenticator[F[_]](implicit F: Sync[F], S: ScalaSodium): MessageAuth[F, A, SodiumMACKey] =
    new MessageAuth[F, A, SodiumMACKey] {

      def algorithm: String = algo

      def sign(in: Array[Byte], key: SodiumMACKey[A]): F[MAC[A]] =
        F.delay(impl.sign(in, key))

      def verifyBool(in: Array[Byte], hashed: MAC[A], key: SodiumMACKey[A]): F[Boolean] =
        F.delay(impl.verify(in, hashed, key))
    }

  object impl {
    final def sign(in: Array[Byte], key: SodiumMACKey[A])(implicit S: ScalaSodium): MAC[A] = {
      val out = new Array[Byte](macLen)
      sodiumSign(in, out, key)
      MAC[A](out)
    }

    final def verify(in: Array[Byte], hashed: MAC[A], key: SodiumMACKey[A])(
        implicit S: ScalaSodium
    ): Boolean =
      sodiumVerify(in, hashed, key) == 0

    final def unsafeGenerateKey(implicit S: ScalaSodium): SodiumMACKey[A] =
      SodiumMACKey[A](ScalaSodium.randomBytesUnsafe(keyLen))

    final def unsafeBuildKey(key: Array[Byte]): SodiumMACKey[A] =
      if (key.length != keyLen)
        throw new IllegalArgumentException("Invalid Key len ") //Better error type maybe?
      else
        SodiumMACKey[A](key)
  }
} 
Example 76
Source File: KeyExchange.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.kx.libsodium

import cats.effect.Sync
import tsec.cipher.symmetric.libsodium.SodiumKey
import tsec.libsodium.ScalaSodium

sealed trait KeyExchange

object KeyExchange {

  def generateKeyPair[F[_]](implicit F: Sync[F], S: ScalaSodium): F[SodiumKeyPair[KeyExchange]] = F.delay {
    val pk = new Array[Byte](ScalaSodium.crypto_kx_PUBLICKEYBYTES)
    val sk = new Array[Byte](ScalaSodium.crypto_kx_SECRETKEYBYTES)

    S.crypto_kx_keypair(pk, sk)

    SodiumKeyPair(PublicKey[KeyExchange](pk), PrivateKey[KeyExchange](sk))
  }

  def generateKeyPairSeed[F[_]](seed: Array[Byte])(implicit F: Sync[F], S: ScalaSodium): F[SodiumKeyPair[KeyExchange]] =
    F.delay {
      if (seed.length != ScalaSodium.crypto_kx_SEEDBYTES)
        throw KeySeedingError(seed.length)

      val pk = new Array[Byte](ScalaSodium.crypto_kx_PUBLICKEYBYTES)
      val sk = new Array[Byte](ScalaSodium.crypto_kx_SECRETKEYBYTES)

      S.crypto_kx_seed_keypair(pk, sk, seed)

      SodiumKeyPair(PublicKey[KeyExchange](pk), PrivateKey[KeyExchange](sk))
    }

  def generateClientSessionKeys[F[_]](
      keyPair: SodiumKeyPair[KeyExchange],
      server: PublicKey[KeyExchange]
  )(implicit F: Sync[F], S: ScalaSodium): F[SodiumSharedKeyPair[KeyExchange]] = F.delay {
    val rx = new Array[Byte](ScalaSodium.crypto_kx_SESSIONKEYBYTES)
    val tx = new Array[Byte](ScalaSodium.crypto_kx_SESSIONKEYBYTES)

    if (S.crypto_kx_client_session_keys(rx, tx, keyPair.pubKey, keyPair.privKey, server) != 0)
      throw KeySessionError

    SodiumSharedKeyPair[KeyExchange](SodiumKey(rx), SodiumKey(tx))
  }

  def generateServerSessionKeys[F[_]](
      keyPair: SodiumKeyPair[KeyExchange],
      client: PublicKey[KeyExchange]
  )(implicit F: Sync[F], S: ScalaSodium): F[SodiumSharedKeyPair[KeyExchange]] = F.delay {
    val rx = new Array[Byte](ScalaSodium.crypto_kx_SESSIONKEYBYTES)
    val tx = new Array[Byte](ScalaSodium.crypto_kx_SESSIONKEYBYTES)

    if (S.crypto_kx_server_session_keys(rx, tx, keyPair.pubKey, keyPair.privKey, client) != 0)
      throw KeySessionError

    SodiumSharedKeyPair[KeyExchange](SodiumKey(rx), SodiumKey(tx))
  }
} 
Example 77
Source File: ProtectedResource.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.oauth2.provider

import cats.implicits._
import cats.data.EitherT
import cats.effect.Sync
import tsec.oauth2.provider.AccessTokenFetcher._

object ProtectedResource {
  def apply[F[_], U](handler: ProtectedResourceHandler[F, U]): ProtectedResource[F, U] =
    new ProtectedResource[F, U](handler)
}

class ProtectedResource[F[_], U](handler: ProtectedResourceHandler[F, U]) {
  val fetchers = Set(RequestParameter, AuthHeader)

  def authorize(
      request: ProtectedResourceRequest
  )(implicit F: Sync[F]): EitherT[F, OAuthError, AuthInfo[U]] =
    for {
      result <- EitherT.fromEither[F](
        fetchers
          .find { fetcher =>
            fetcher.matches(request)
          }
          .toRight(InvalidRequest("Access token is not found"))
          .flatMap(x => x.fetch(request))
      )
      token <- EitherT[F, OAuthError, AccessToken](
        handler.findAccessToken(result.token).map(_.toRight[OAuthError](InvalidToken("The access token is not found")))
      )
      _ <- EitherT(token.isExpired.map(expired => Either.cond(!expired, (), ExpiredToken)))
      authInfo <- EitherT[F, OAuthError, AuthInfo[U]](
        handler.findAuthInfoByAccessToken(token).map(_.toRight[OAuthError](InvalidToken("The access token is invalid")))
      )
    } yield authInfo
} 
Example 78
Source File: AuthorizationCodeGrantHandler.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.oauth2.provider
package grantHandler

import cats.data.EitherT
import cats.effect.Sync
import cats.implicits._
import tsec.oauth2.provider.ValidatedRequest._

class AuthorizationCodeGrantHandler[F[_], U](handler: AuthorizationCodeHandler[F, U]) extends GrantHandler[F, U] {
  type A = ValidatedAuthorizationCode
  def handleRequest(
      req: ValidatedAuthorizationCode
  )(implicit F: Sync[F]): EitherT[F, OAuthError, GrantHandlerResult[U]] =
    for {
      _ <- EitherT(
        handler
          .validateClient(req)
          .map(
            isValid => Either.cond(isValid, (), InvalidClient("Invalid client or client is not authorized"): OAuthError)
          )
      )
      auth <- EitherT[F, OAuthError, AuthInfo[U]](
        handler
          .findAuthInfoByCode(req.code)
          .map(_.toRight(InvalidGrant("Authorized information is not found by the code")))
      )
      _ <- EitherT
        .cond[F](auth.clientId.contains(req.clientCredential.clientId), (), InvalidClient("invalid clientId"))
      _ <- EitherT.cond[F](
        auth.redirectUri.isEmpty || (auth.redirectUri.isDefined && auth.redirectUri == req.redirectUri),
        (),
        RedirectUriMismatch
      )
      grantResult <- EitherT(
        issueAccessToken(handler, auth).attempt
          .map(_.leftMap(t => FailedToIssueAccessToken(t.getMessage): OAuthError))
      )
      _ <- EitherT(
        handler.deleteAuthCode(req.code).attempt.map(_.leftMap(t => FailedToDeleteAuthCode(t.getMessage): OAuthError))
      )
    } yield grantResult
}

trait AuthorizationCodeHandler[F[_], U] extends IssueAccessToken[F, U] {

  
  def deleteAuthCode(code: String): F[Unit]
} 
Example 79
Source File: ClientCredentialsGrantHandler.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.oauth2.provider
package grantHandler

import cats.data.EitherT
import cats.effect.Sync
import cats.implicits._
import tsec.oauth2.provider.ValidatedRequest._

class ClientCredentialsGrantHandler[F[_], U](handler: ClientCredentialsHandler[F, U]) extends GrantHandler[F, U] {
  type A = ValidatedClientCredentials
  def handleRequest(
      req: ValidatedClientCredentials
  )(implicit F: Sync[F]): EitherT[F, OAuthError, GrantHandlerResult[U]] =
    for {
      _ <- EitherT[F, OAuthError, Unit](
        handler
          .validateClient(req)
          .map(
            isValid => Either.cond(isValid, (), InvalidClient("Invalid client or client is not authorized"): OAuthError)
          )
      )
      user <- EitherT[F, OAuthError, U](
        handler
          .findUser(req)
          .map(_.toRight(InvalidGrant("client_id or client_secret or scope is incorrect")))
      )
      authInfo = AuthInfo(user, Some(req.clientCredential.clientId), req.scope, None)
      grantResult <- EitherT(
        issueAccessToken(handler, authInfo).attempt
          .map(_.leftMap(t => FailedToIssueAccessToken(t.getMessage): OAuthError))
      )
    } yield grantResult
}

trait ClientCredentialsHandler[F[_], U] extends IssueAccessToken[F, U] {

  
  def refreshAccessToken(authInfo: AuthInfo[U], refreshToken: String): F[AccessToken]
} 
Example 80
Source File: ImplicitGrantHandler.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.oauth2.provider
package grantHandler

import cats.implicits._
import cats.data.EitherT
import cats.effect.Sync
import tsec.oauth2.provider.ValidatedRequest._

class ImplicitGrantHandler[F[_], U](handler: ImplicitHandler[F, U]) extends GrantHandler[F, U] {
  type A = ValidatedImplicit
  def handleRequest(
      req: ValidatedImplicit
  )(implicit F: Sync[F]): EitherT[F, OAuthError, GrantHandlerResult[U]] =
    for {
      _ <- EitherT[F, OAuthError, Unit](
        handler
          .validateClient(req)
          .map(
            isValid => Either.cond(isValid, (), InvalidClient("Invalid client or client is not authorized"): OAuthError)
          )
      )
      user <- EitherT[F, OAuthError, U](
        handler.findUser(req).map(_.toRight(InvalidGrant("user cannot be authenticated")))
      )

      authInfo = AuthInfo(user, Some(req.clientCredential.clientId), req.scope, None)
      token = handler.getStoredAccessToken(authInfo).flatMap { token =>
        val res = token match {
          case Some(token) => F.pure(token)
          case None        => handler.createAccessToken(authInfo)
        }
        for {
          t         <- res
          expiresIn <- t.expiresIn
        } yield
          GrantHandlerResult(
            authInfo,
            "Bearer",
            t.token,
            expiresIn,
            None,
            t.scope,
            t.params
          )
      }
      grantResult <- EitherT(
        token.attempt
          .map(_.leftMap(t => FailedToIssueAccessToken(t.getMessage): OAuthError))
      )
    } yield grantResult
}

trait ImplicitHandler[F[_], U] {

  
  def getStoredAccessToken(authInfo: AuthInfo[U]): F[Option[AccessToken]]
} 
Example 81
Source File: RrefreshTokenHandler.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.oauth2.provider
package grantHandler

import cats.data.EitherT
import cats.effect.Sync
import cats.implicits._
import tsec.oauth2.provider.ValidatedRequest._

class RefreshTokenGrantHandler[F[_], U](handler: RefreshTokenHandler[F, U]) extends GrantHandler[F, U] {
  type A = ValidatedRefreshToken
  def handleRequest(req: ValidatedRefreshToken)(implicit F: Sync[F]): EitherT[F, OAuthError, GrantHandlerResult[U]] =
    for {
      _ <- EitherT[F, OAuthError, Unit](
        handler
          .validateClient(req)
          .map(
            isValid => Either.cond(isValid, (), InvalidClient("Invalid client or client is not authorized"): OAuthError)
          )
      )
      auth <- EitherT[F, OAuthError, AuthInfo[U]](
        handler
          .findAuthInfoByRefreshToken(req.refreshToken)
          .map(_.toRight(InvalidGrant("Authorized information is not found by the refresh token")))
      )
      _ <- EitherT
        .cond[F](auth.clientId.contains(req.clientCredential.clientId), (), InvalidClient("invalid clientId"))
      token <- EitherT(
        handler
          .refreshAccessToken(auth, req.refreshToken)
          .attempt
          .map(_.leftMap(t => RefreshTokenFailed(t.getMessage): OAuthError))
      )
      grantResult <- EitherT(
        token.expiresIn
          .map(GrantHandler.createGrantHandlerResult(auth, token, _))
          .attempt
          .map(_.leftMap(t => RefreshTokenFailed(t.getMessage): OAuthError))
      )
    } yield grantResult
}

trait RefreshTokenHandler[F[_], U] {

  
  def findAuthInfoByRefreshToken(refreshToken: String): F[Option[AuthInfo[U]]]
} 
Example 82
Source File: PasswordNoClientCredGrantHandler.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.oauth2.provider
package grantHandler

import cats.data.EitherT
import cats.effect.Sync
import cats.implicits._
import tsec.oauth2.provider.ValidatedRequest._

class PasswordNoClientCredGrantHandler[F[_], U](handler: PasswordNoClientCredHandler[F, U]) extends GrantHandler[F, U] {
  type A = ValidatedPasswordNoClientCred
  def handleRequest(
      req: ValidatedPasswordNoClientCred
  )(implicit F: Sync[F]): EitherT[F, OAuthError, GrantHandlerResult[U]] =
    for {
      user <- EitherT[F, OAuthError, U](
        handler.findUser(None, req).map(_.toRight(InvalidGrant("username or password is incorrect")))
      )
      authInfo = AuthInfo(user, None, req.scope, None)
      grantResult <- EitherT(
        issueAccessToken(handler, authInfo).attempt
          .map(_.leftMap(t => FailedToIssueAccessToken(t.getMessage): OAuthError))
      )
    } yield grantResult
}

trait PasswordNoClientCredHandler[F[_], U] extends IssueAccessToken[F, U] {

  
  def findUser(maybeCredential: Option[ClientCredential], request: ValidatedPasswordNoClientCred): F[Option[U]]
} 
Example 83
Source File: GrantHandler.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.oauth2.provider
package grantHandler

import cats.data.EitherT
import cats.effect.Sync
import cats.implicits._

import scala.concurrent.duration.FiniteDuration

sealed abstract class GrantType extends Product with Serializable {
  def name: String
}

object GrantType {
  val header = "grant_type"

  case object AuthorizationCode extends GrantType {
    def name: String = "authorization_code"
  }

  case object RefreshToken extends GrantType {
    def name: String = "refresh_token"
  }

  case object ClientCrendentials extends GrantType {
    def name: String = "client_credentials"
  }

  case object Password extends GrantType {
    def name: String = "password"
  }

  case object Implicit extends GrantType {
    def name: String = "implicit"
  }

  val strToGrantType = Map(
    AuthorizationCode.name  -> AuthorizationCode,
    RefreshToken.name       -> RefreshToken,
    ClientCrendentials.name -> ClientCrendentials,
    Password.name           -> Password,
    Implicit.name           -> Implicit
  )
}

final case class GrantHandlerResult[U](
    authInfo: AuthInfo[U],
    tokenType: String,
    accessToken: String,
    expiresIn: Option[FiniteDuration],
    refreshToken: Option[String],
    scope: Option[String],
    params: Map[String, String]
)

trait GrantHandler[F[_], U] {
  type A
  def handleRequest(req: A)(implicit F: Sync[F]): EitherT[F, OAuthError, GrantHandlerResult[U]]

  
  def refreshAccessToken(authInfo: AuthInfo[U], refreshToken: String): F[AccessToken]
} 
Example 84
Source File: JCAPrimitiveCipher.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.cipher.symmetric.jca.primitive

import javax.crypto.{Cipher => JCipher}

import cats.MonadError
import cats.effect.Sync
import cats.syntax.all._
import tsec.cipher.common.padding.SymmetricPadding
import tsec.cipher.symmetric.{Encryptor, _}
import tsec.cipher.symmetric.jca.{IvProcess, SecretKey}

sealed abstract class JCAPrimitiveCipher[F[_], C, M, P](
    implicit algoTag: BlockCipher[C],
    modeSpec: CipherMode[M],
    paddingTag: SymmetricPadding[P],
    private[tsec] val ivProcess: IvProcess[C, M, P]
) extends Encryptor[F, C, SecretKey] {

  private[tsec] def catchF[Y](a: => Y): F[Y]

  private def getInstance =
    JCAPrimitiveCipher.getJCipherUnsafe[C, M, P]

  def encrypt(plainText: PlainText, key: SecretKey[C], iv: Iv[C]): F[CipherText[C]] =
    catchF {
      val instance = getInstance
      ivProcess.encryptInit(instance, iv, key.toJavaKey)
      val encrypted = instance.doFinal(plainText)
      CipherText[C](RawCipherText(encrypted), iv)
    }

  def decrypt(cipherText: CipherText[C], key: SecretKey[C]): F[PlainText] = catchF {
    val instance = getInstance
    ivProcess.decryptInit(instance, cipherText.nonce, key.toJavaKey)
    val out = instance.doFinal(cipherText.content)
    PlainText(out)
  }
}

object JCAPrimitiveCipher {

  private[tsec] def getJCipherUnsafe[A, M, P](
      implicit algoTag: BlockCipher[A],
      modeSpec: CipherMode[M],
      paddingTag: SymmetricPadding[P]
  ): JCipher = JCipher.getInstance(s"${algoTag.cipherName}/${modeSpec.mode}/${paddingTag.algorithm}")

  def sync[F[_], A: BlockCipher, M: CipherMode, P: SymmetricPadding](
      implicit F: Sync[F],
      ivProcess: IvProcess[A, M, P]
  ): JCAPrimitiveCipher[F, A, M, P] =
    new JCAPrimitiveCipher[F, A, M, P] {
      private[tsec] def catchF[C](thunk: => C): F[C] = F.delay(thunk)
    }

  def monadError[F[_], A: BlockCipher, M: CipherMode, P: SymmetricPadding](
      implicit F: MonadError[F, Throwable],
      ivProcess: IvProcess[A, M, P]
  ): JCAPrimitiveCipher[F, A, M, P] =
    new JCAPrimitiveCipher[F, A, M, P] {
      private[tsec] def catchF[C](thunk: => C): F[C] = F.catchNonFatal(thunk)
    }
} 
Example 85
Source File: IvStrategy.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.cipher.symmetric.jca

import cats.Applicative
import cats.effect.Sync
import tsec.cipher.symmetric._
import tsec.common.ManagedRandom

object JCAIvGen {
  def random[F[_], A](implicit C: BlockCipher[A], F: Sync[F]): IvGen[F, A] =
    new IvGen[F, A] with ManagedRandom {

      def genIv: F[Iv[A]] =
        F.delay(genIvUnsafe)

      def genIvUnsafe: Iv[A] = {
        val nonce = new Array[Byte](C.blockSizeBytes)
        nextBytes(nonce)
        Iv[A](nonce)
      }
    }

  def emptyIv[F[_], A](implicit F: Applicative[F]): IvGen[F, A] =
    new IvGen[F, A] {
      protected val cachedEmpty = Array.empty[Byte]

      def genIv: F[Iv[A]] =
        F.pure(Iv[A](cachedEmpty))

      def genIvUnsafe: Iv[A] = Iv[A](cachedEmpty)
    }
}

trait CounterIvGen[F[_], A] extends IvGen[F, A] {
  def refresh: F[Unit]

  def counterState: F[Long]

  def unsafeCounterState: Long
} 
Example 86
Source File: AESCTR.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.cipher.symmetric.jca

import java.util.concurrent.atomic.AtomicLong

import cats.effect.Sync
import tsec.cipher.common.padding.NoPadding
import tsec.cipher.symmetric._
import tsec.cipher.symmetric.jca.primitive.JCAPrimitiveCipher

sealed abstract class AESCTR[A] extends JCACipherAPI[A, CTR, NoPadding] with AES[A] with JCAKeyGen[A] {
  implicit val ae: AESCTR[A] = this

  implicit def genEncryptor[F[_]: Sync](implicit c: BlockCipher[A]): JCAPrimitiveCipher[F, A, CTR, NoPadding] =
    JCAPrimitiveCipher.sync[F, A, CTR, NoPadding]

  
  def incrementalIvStrategy[F[_]](implicit F: Sync[F]): CounterIvGen[F, A] =
    new CounterIvGen[F, A] {
      private val delta                     = 1000000L
      private val maxVal: Long              = Long.MaxValue - delta
      private val fixedCounter: Array[Byte] = Array.fill[Byte](8)(0.toByte)
      private val atomicNonce: AtomicLong   = new AtomicLong(Long.MinValue)

      def refresh: F[Unit] = F.delay(atomicNonce.set(Long.MinValue))

      def counterState: F[Long] = F.delay(unsafeCounterState)

      def unsafeCounterState: Long = atomicNonce.get()

      def genIv: F[Iv[A]] =
        F.delay(genIvUnsafe)

      def genIvUnsafe: Iv[A] =
        if (atomicNonce.get() >= maxVal) {
          throw IvError("Maximum safe nonce number reached")
        } else {
          val nonce = atomicNonce.incrementAndGet()
          val iv    = new Array[Byte](16) //AES block size
          iv(0) = (nonce >> 56).toByte
          iv(1) = (nonce >> 48).toByte
          iv(2) = (nonce >> 40).toByte
          iv(3) = (nonce >> 32).toByte
          iv(4) = (nonce >> 24).toByte
          iv(5) = (nonce >> 16).toByte
          iv(6) = (nonce >> 8).toByte
          iv(7) = nonce.toByte
          System.arraycopy(fixedCounter, 0, iv, 8, 8)
          Iv[A](iv)
        }
    }

  def ciphertextFromConcat(rawCT: Array[Byte]): Either[CipherTextError, CipherText[A]] =
    CTOPS.ciphertextFromArray[A, CTR, NoPadding](rawCT)
}

sealed trait AES128CTR

object AES128CTR extends AESCTR[AES128CTR] with AES128[AES128CTR]

sealed trait AES192CTR

object AES192CTR extends AESCTR[AES192CTR] with AES192[AES192CTR]

sealed trait AES256CTR

object AES256CTR extends AESCTR[AES256CTR] with AES256[AES256CTR] 
Example 87
Source File: AESGCM.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec.cipher.symmetric.jca

import java.util.concurrent.atomic.AtomicInteger

import cats.effect.Sync
import tsec.cipher.common.padding.NoPadding
import tsec.cipher.symmetric._
import tsec.cipher.symmetric.jca.primitive.JCAAEADPrimitive

sealed abstract class AESGCM[A] extends JCAAEAD[A, GCM, NoPadding] with AES[A] with JCAKeyGen[A] {
  implicit val ae: AESGCM[A] = this

  implicit def genEncryptor[F[_]: Sync](implicit c: AES[A]): AADEncryptor[F, A, SecretKey] =
    JCAAEADPrimitive.sync[F, A, GCM, NoPadding]

  
  def incrementalIvStrategy[F[_]](implicit F: Sync[F]): CounterIvGen[F, A] =
    new CounterIvGen[F, A] {
      private val delta                      = 1000000
      private val maxVal: Int                = Int.MaxValue - delta
      private val fixedCounter: Array[Byte]  = Array.fill[Byte](8)(0.toByte)
      private val atomicNonce: AtomicInteger = new AtomicInteger(Int.MinValue)

      def refresh: F[Unit] = F.delay(atomicNonce.set(Int.MinValue))

      def counterState: F[Long] = F.delay(unsafeCounterState)

      def unsafeCounterState: Long = atomicNonce.get().toLong

      def genIv: F[Iv[A]] =
        F.delay(genIvUnsafe)

      def genIvUnsafe: Iv[A] =
        if (atomicNonce.get() >= maxVal)
          throw IvError("Maximum safe nonce number reached")
        else {
          val nonce = atomicNonce.incrementAndGet()
          val iv    = new Array[Byte](12) //GCM optimal iv len
          iv(0) = (nonce >> 24).toByte
          iv(1) = (nonce >> 16).toByte
          iv(2) = (nonce >> 8).toByte
          iv(3) = nonce.toByte
          System.arraycopy(fixedCounter, 0, iv, 4, 8)
          Iv[A](iv)
        }
    }

  def ciphertextFromConcat(rawCT: Array[Byte]): Either[CipherTextError, CipherText[A]] =
    CTOPS.ciphertextFromArray[A, GCM, NoPadding](rawCT)
}

sealed trait AES128GCM

object AES128GCM extends AESGCM[AES128GCM] with AES128[AES128GCM]

sealed trait AES192GCM

object AES192GCM extends AESGCM[AES192GCM] with AES192[AES192GCM]

sealed trait AES256GCM

object AES256GCM extends AESGCM[AES256GCM] with AES256[AES256GCM] 
Example 88
Source File: KamonSupport.scala    From kamon-http4s   with Apache License 2.0 5 votes vote down vote up
package kamon.http4s
package middleware.server

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

object KamonSupport {

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

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


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

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

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


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

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

} 
Example 89
Source File: FileUtils.scala    From skeuomorph   with Apache License 2.0 5 votes vote down vote up
package higherkindness.skeuomorph

import java.io.{File, FileOutputStream, InputStream}
import java.nio.file.{Files, Paths, StandardOpenOption}

import cats.effect.{Resource, Sync}

object FileUtils {
  def fileHandle[F[_]: Sync](name: String): Resource[F, File] =
    Resource.make(
      Sync[F].delay(new File(name))
    )(file => Sync[F].delay(file.deleteOnExit()))

  def fileOutputStream[F[_]: Sync](file: File): Resource[F, FileOutputStream] =
    Resource.make(
      Sync[F].delay(new FileOutputStream(file))
    )(fos => Sync[F].delay(fos.close()))

  def fileInputStream[F[_]: Sync](name: String): Resource[F, InputStream] =
    Resource.make(
      Sync[F].delay(Files.newInputStream(Paths.get(name), StandardOpenOption.DELETE_ON_CLOSE))
    )(is => Sync[F].delay(is.close()))
} 
Example 90
Source File: ParseOpenApi.scala    From skeuomorph   with Apache License 2.0 5 votes vote down vote up
package higherkindness.skeuomorph.openapi

import java.io.File

import higherkindness.droste._
import higherkindness.skeuomorph.Parser
import schema.OpenApi
import cats.effect.Sync
import cats.syntax.flatMap._
import cats.syntax.functor._
import cats.syntax.either._

object ParseOpenApi {
  import JsonDecoders._

  case class YamlSource(file: File)
  case class JsonSource(file: File)

  implicit def parseYamlOpenApi[F[_], T](implicit T: Embed[JsonSchemaF, T]): Parser[F, YamlSource, OpenApi[T]] =
    new Parser[F, YamlSource, OpenApi[T]] {
      import yaml.{Decoder => _, _}
      override def parse(input: YamlSource)(implicit S: Sync[F]): F[OpenApi[T]] =
        readContent(input.file).flatMap(x =>
          S.fromEither(
            yaml
              .Decoder[OpenApi[T]]
              .apply(x)
              .left
              .map(_.valueOr(identity))
          )
        )
    }

  implicit def parseJsonOpenApi[F[_], T](implicit T: Embed[JsonSchemaF, T]): Parser[F, JsonSource, OpenApi[T]] =
    new Parser[F, JsonSource, OpenApi[T]] {
      import io.circe.Decoder
      import io.circe.parser

      override def parse(input: JsonSource)(implicit S: Sync[F]): F[OpenApi[T]] =
        for {
          content <- readContent(input.file)
          json    <- S.fromEither(parser.parse(content))
          openApi <- S.fromEither(Decoder[OpenApi[T]].decodeJson(json))
        } yield openApi
    }

  private def readContent[F[_]: Sync](file: File): F[String] =
    Sync[F].delay {
      scala.io.Source
        .fromFile(file)
        .getLines()
        .toList
        .mkString("\n")
    }

} 
Example 91
Source File: Http4sTelegramClient.scala    From canoe   with MIT License 5 votes vote down vote up
package canoe.api.clients

import canoe.api.{FailedMethod, ResponseDecodingError, TelegramClient}
import canoe.methods.Method
import canoe.models.{InputFile, Response => TelegramResponse}
import cats.effect.Sync
import cats.syntax.all._
import fs2.Stream
import io.chrisdavenport.log4cats.Logger
import org.http4s._
import org.http4s.circe._
import org.http4s.client.Client
import org.http4s.client.dsl.Http4sClientDsl._
import org.http4s.multipart.{Multipart, Part}

private[api] class Http4sTelegramClient[F[_]: Sync: Logger](token: String, client: Client[F])
    extends TelegramClient[F] {

  private val botApiUri: Uri = Uri.unsafeFromString("https://api.telegram.org") / s"bot$token"

  def execute[Req, Res](request: Req)(implicit M: Method[Req, Res]): F[Res] = {

    val req = prepareRequest(botApiUri / M.name, M, request)

    implicit val decoder: EntityDecoder[F, TelegramResponse[Res]] =
      jsonOf(Sync[F], TelegramResponse.decoder(M.decoder))

    F.debug(s"Executing '${M.name}' Telegram method.") *>
      client
        .expect[TelegramResponse[Res]](req)
        .recoverWith { case error: InvalidMessageBodyFailure => handleUnknownEntity(M.name, request, error) }
        .flatMap(handleTelegramResponse(M, request))
  }

  private def handleUnknownEntity[I, A](method: String, input: I, error: InvalidMessageBodyFailure): F[A] =
    F.error(
      s"Received unknown Telegram entity during execution of '$method' method. \nInput data: $input. \n${error.details}"
    ) *>
      ResponseDecodingError(error.details.dropWhile(_ != '{')).raiseError[F, A]

  private def prepareRequest[Req, Res](url: Uri, method: Method[Req, Res], action: Req): F[Request[F]] = {
    val uploads = method.attachments(action).collect {
      case (name, InputFile.Upload(filename, contents)) =>
        Part.fileData(name, filename, Stream.emits(contents).covary[F])
    }

    if (uploads.isEmpty) jsonRequest(url, method, action)
    else multipartRequest(url, method, action, uploads)
  }

  private def jsonRequest[Req, Res](url: Uri, method: Method[Req, Res], action: Req): F[Request[F]] =
    Method.POST(action, url)(F, jsonEncoderOf(method.encoder))

  private def multipartRequest[Req, Res](url: Uri,
                                         method: Method[Req, Res],
                                         action: Req,
                                         parts: List[Part[F]]): F[Request[F]] = {
    val multipart = Multipart[F](parts.toVector)

    val params =
      method
        .encoder(action)
        .asObject
        .map(
          _.toIterable
            .filterNot(kv => kv._2.isNull || kv._2.isObject)
            .map { case (k, j) => k -> j.toString }
            .toMap
        )
        .getOrElse(Map.empty)

    val urlWithQueryParams = params.foldLeft(url) {
      case (url, (key, value)) => url.withQueryParam(key, value)
    }

    Method.POST(multipart, urlWithQueryParams).map(_.withHeaders(multipart.headers))
  }

  private def handleTelegramResponse[A, I, C](m: Method[I, A], input: I)(response: TelegramResponse[A]): F[A] =
    response match {
      case TelegramResponse(true, Some(result), _, _, _) => result.pure[F]

      case failed =>
        F.error(s"Received failed response from Telegram: $failed. Method name: ${m.name}, input data: $input") *>
          FailedMethod(m, input, failed).raiseError[F, A]
    }
} 
Example 92
Source File: Http4sClientEndpointsJsonSchemaTest.scala    From endpoints4s   with MIT License 5 votes vote down vote up
package endpoints4s.http4s.client

import endpoints4s.algebra
import endpoints4s.algebra.client

import cats.effect.Sync
import org.http4s.client.Client
import cats.effect.IO
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.global
import org.http4s.client.asynchttpclient.AsyncHttpClient
import cats.effect.ContextShift
import endpoints4s.algebra.circe
import org.http4s.Uri

class TestJsonSchemaClient[F[_]: Sync](host: Uri, client: Client[F])
    extends Endpoints[F](host, client)
    with BasicAuthentication
    with JsonEntitiesFromCodecs
    with algebra.BasicAuthenticationTestApi
    with algebra.EndpointsTestApi
    with algebra.JsonFromCodecTestApi
    with algebra.SumTypedEntitiesTestApi
    with circe.JsonFromCirceCodecTestApi
    with circe.JsonEntitiesFromCodecs

class Http4sClientEndpointsJsonSchemaTest
    extends client.EndpointsTestSuite[TestJsonSchemaClient[IO]]
    with client.BasicAuthTestSuite[TestJsonSchemaClient[IO]]
    with client.JsonFromCodecTestSuite[TestJsonSchemaClient[IO]]
    with client.SumTypedEntitiesTestSuite[TestJsonSchemaClient[IO]] {

  implicit val ctx: ContextShift[IO] = IO.contextShift(global)

  val (ahc, shutdown) =
    AsyncHttpClient.allocate[IO]().unsafeRunSync()

  val client = new TestJsonSchemaClient[IO](
    Uri.unsafeFromString(s"http://localhost:$wiremockPort"),
    ahc
  )

  def call[Req, Resp](
      endpoint: client.Endpoint[Req, Resp],
      args: Req
  ): Future[Resp] = {
    Thread.sleep(50)
    val eventualResponse = endpoint(args)
    Thread.sleep(50)
    eventualResponse.unsafeToFuture()
  }

  def encodeUrl[A](url: client.Url[A])(a: A): String =
    url.encodeUrl(a).toOption.get.renderString

  clientTestSuite()
  basicAuthSuite()
  jsonFromCodecTestSuite()

  override def afterAll(): Unit = {
    shutdown.unsafeRunSync()
    super.afterAll()
  }

} 
Example 93
Source File: RerunnableContextShiftSuite.scala    From catbird   with Apache License 2.0 5 votes vote down vote up
package io.catbird.util.effect

import cats.effect.{ ContextShift, IO, Sync }
import com.twitter.util.{ Await, Future, FuturePool }
import io.catbird.util.Rerunnable
import org.scalatest.Outcome
import org.scalatest.funsuite.FixtureAnyFunSuite

class RerunnableContextShiftSuite extends FixtureAnyFunSuite with ThreadPoolNamingSupport {

  protected final class FixtureParam {
    val futurePoolName = "future-pool"
    val otherPoolName = "other-pool"
    val ioPoolName = "io-pool"

    val futurePool = FuturePool.interruptible(newNamedThreadPool(futurePoolName))
    val otherPool = newNamedThreadPool(otherPoolName)
    val ioPool = newNamedThreadPool(ioPoolName)

    def newIO: IO[String] = IO(currentThreadName())

    def newFuture: Future[String] = futurePool(currentThreadName())

    def newRerunnable: Rerunnable[String] = Rerunnable(currentThreadName())
  }

  test("ContextShift[Rerunnable].shift shifts to the pool of the instance") { f =>
    implicit val cs: ContextShift[Rerunnable] =
      RerunnableContextShift.fromExecutionContext(f.ioPool)

    val (poolName1, poolName2, poolName3) =
      (for {
        poolName1 <- Rerunnable.fromFuture(f.newFuture)

        _ <- ContextShift[Rerunnable](cs).shift

        poolName2 <- Sync[Rerunnable].delay(currentThreadName())

        poolName3 <- Rerunnable.fromFuture(f.newFuture)
      } yield (poolName1, poolName2, poolName3)).run.await

    assert(poolName1 == f.futurePoolName)
    assert(poolName2 == f.ioPoolName)
    assert(poolName2 == f.ioPoolName)
  }

  test("ContextShift[Rerunnable].evalOn executes on correct pool and shifts back to previous pool") { f =>
    implicit val cs: ContextShift[Rerunnable] =
      RerunnableContextShift.fromExecutionContext(f.ioPool)

    val (poolName1, poolName2, poolName3) =
      (for {
        poolName1 <- f.newRerunnable

        poolName2 <- ContextShift[Rerunnable].evalOn(f.otherPool)(f.newRerunnable)

        poolName3 <- f.newRerunnable
      } yield (poolName1, poolName2, poolName3)).run.await

    assert(poolName1 == currentThreadName()) // The first rerunnable is not explicitly evaluated on a dedicated pool
    assert(poolName2 == f.otherPoolName)
    assert(poolName3 == f.ioPoolName)
  }

  test("ContextShift[Rerunnable].evalOn executes on correct pool and shifts back to future pool") { f =>
    implicit val cs: ContextShift[Rerunnable] =
      RerunnableContextShift.fromExecutionContext(f.ioPool)

    val (poolName1, poolName2, poolName3) =
      (for {
        poolName1 <- Rerunnable.fromFuture(f.newFuture) // The future was started on a dedicated pool (e.g. netty)

        poolName2 <- ContextShift[Rerunnable].evalOn(f.otherPool)(f.newRerunnable)

        poolName3 <- Rerunnable.fromFuture(f.newFuture)
      } yield (poolName1, poolName2, poolName3)).run.await

    assert(poolName1 == f.futurePoolName)
    assert(poolName2 == f.otherPoolName)
    assert(poolName3 == f.futurePoolName)
  }

  implicit private class FutureAwaitOps[A](future: Future[A]) {
    def await: A = Await.result(future)
  }

  override protected def withFixture(test: OneArgTest): Outcome = withFixture(test.toNoArgTest(new FixtureParam))
} 
Example 94
Source File: package.scala    From fs2-blobstore   with Apache License 2.0 5 votes vote down vote up
import java.io.OutputStream
import java.nio.file.Files

import cats.effect.{Blocker, Concurrent, ContextShift, Resource, Sync}
import fs2.{Chunk, Hotswap, Pipe, Pull, RaiseThrowable, Stream}
import cats.implicits._

package object blobstore {
  protected[blobstore] def _writeAllToOutputStream1[F[_]](in: Stream[F, Byte], out: OutputStream, blocker: Blocker)(
    implicit F: Sync[F],
    CS: ContextShift[F]
  ): Pull[F, Nothing, Unit] = {
    in.pull.uncons.flatMap {
      case None => Pull.done
      case Some((hd, tl)) =>
        Pull.eval[F, Unit](blocker.delay(out.write(hd.toArray))) >> _writeAllToOutputStream1(tl, out, blocker)
    }
  }

  protected[blobstore] def bufferToDisk[F[_]](
    chunkSize: Int,
    blocker: Blocker
  )(implicit F: Sync[F], CS: ContextShift[F]): Pipe[F, Byte, (Long, Stream[F, Byte])] = { in =>
    Stream.bracket(F.delay(Files.createTempFile("bufferToDisk", ".bin")))(p => F.delay(p.toFile.delete).void).flatMap {
      p =>
        in.through(fs2.io.file.writeAll(p, blocker)).drain ++
          Stream.emit((p.toFile.length, fs2.io.file.readAll(p, blocker, chunkSize)))
    }
  }

  private[blobstore] def putRotateBase[F[_]: Concurrent, T](
    limit: Long,
    openNewFile: Resource[F, T]
  )(consume: T => Chunk[Byte] => F[Unit]): Pipe[F, Byte, Unit] = { in =>
    Stream
      .resource(Hotswap(openNewFile))
      .flatMap {
        case (hotswap, newFile) =>
          goRotate(limit, 0L, in, newFile, hotswap, openNewFile)(
            consume = consumer => bytes => Pull.eval(consume(consumer)(bytes)).as(consumer),
            extract = Stream.emit
          ).stream
      }
  }

  private[blobstore] def goRotate[F[_]: RaiseThrowable, A, B](
    limit: Long,
    acc: Long,
    s: Stream[F, Byte],
    consumer: B,
    hotswap: Hotswap[F, A],
    resource: Resource[F, A]
  )(
    consume: B => Chunk[Byte] => Pull[F, Unit, B],
    extract: A => Stream[F, B]
  ): Pull[F, Unit, Unit] = {
    val toWrite = (limit - acc).min(Int.MaxValue.toLong).toInt
    s.pull.unconsLimit(toWrite).flatMap {
      case Some((hd, tl)) =>
        val newAcc = acc + hd.size
        consume(consumer)(hd).flatMap { consumer =>
          if (newAcc >= limit) {
            Pull
              .eval(hotswap.swap(resource))
              .flatMap(a => extract(a).pull.headOrError)
              .flatMap(nc => goRotate(limit, 0L, tl, nc, hotswap, resource)(consume, extract))
          } else {
            goRotate(limit, newAcc, tl, consumer, hotswap, resource)(consume, extract)
          }
        }
      case None => Pull.done
    }
  }

} 
Example 95
Source File: Slf4jLogger.scala    From log4cats   with Apache License 2.0 5 votes vote down vote up
package io.chrisdavenport.log4cats.slf4j

import cats.effect.Sync
import io.chrisdavenport.log4cats.SelfAwareStructuredLogger
import io.chrisdavenport.log4cats.slf4j.internal._
import org.slf4j.{Logger => JLogger}
import language.experimental.macros

object Slf4jLogger {

  def getLogger[F[_]: Sync]: SelfAwareStructuredLogger[F] =
    macro GetLoggerMacros.unsafeCreateImpl[F[_]]

  @deprecated("0.3.0", "Use getLogger instead")
  def unsafeCreate[F[_]: Sync]: SelfAwareStructuredLogger[F] =
    macro GetLoggerMacros.unsafeCreateImpl[F[_]]

  def getLoggerFromName[F[_]: Sync](name: String): SelfAwareStructuredLogger[F] =
    getLoggerFromSlf4j(org.slf4j.LoggerFactory.getLogger(name))

  @deprecated("0.3.0", "Use getLoggerFromName")
  def unsafeFromName[F[_]: Sync](name: String): SelfAwareStructuredLogger[F] =
    getLoggerFromName[F](name)

  def getLoggerFromClass[F[_]: Sync](clazz: Class[_]): SelfAwareStructuredLogger[F] =
    getLoggerFromSlf4j[F](org.slf4j.LoggerFactory.getLogger(clazz))

  @deprecated("0.3.0", "Use getLoggerFromClass")
  def unsafeFromClass[F[_]: Sync](clazz: Class[_]): SelfAwareStructuredLogger[F] =
    getLoggerFromClass[F](clazz)

  def getLoggerFromSlf4j[F[_]: Sync](logger: JLogger): SelfAwareStructuredLogger[F] =
    new Slf4jLoggerInternal.Slf4jLogger(logger)

  @deprecated("0.3.0", "Use getLoggerFromSlf4J instead")
  def unsafeFromSlf4j[F[_]: Sync](logger: JLogger): SelfAwareStructuredLogger[F] =
    getLoggerFromSlf4j[F](logger)

  def create[F[_]: Sync]: F[SelfAwareStructuredLogger[F]] =
    macro GetLoggerMacros.safeCreateImpl[F[_]]

  def fromName[F[_]: Sync](name: String): F[SelfAwareStructuredLogger[F]] =
    Sync[F].delay(getLoggerFromName(name))

  def fromClass[F[_]: Sync](clazz: Class[_]): F[SelfAwareStructuredLogger[F]] =
    Sync[F].delay(getLoggerFromClass(clazz))

  def fromSlf4j[F[_]: Sync](logger: JLogger): F[SelfAwareStructuredLogger[F]] =
    Sync[F].delay(getLoggerFromSlf4j[F](logger))

} 
Example 96
Source File: TestingLogger.scala    From log4cats   with Apache License 2.0 5 votes vote down vote up
package io.chrisdavenport.log4cats.testing

import io.chrisdavenport.log4cats.{SelfAwareLogger}
import cats.effect.Sync
import cats.implicits._
import java.util.concurrent.atomic.AtomicReference
import scala.annotation.tailrec

trait TestingLogger[F[_]] extends SelfAwareLogger[F] {
  import TestingLogger.LogMessage
  def logged: F[Vector[LogMessage]]
}

object TestingLogger {

  sealed trait LogMessage {
    def message: String
    def throwOpt: Option[Throwable]
  }

  final case class TRACE(message: String, throwOpt: Option[Throwable]) extends LogMessage
  final case class DEBUG(message: String, throwOpt: Option[Throwable]) extends LogMessage
  final case class INFO(message: String, throwOpt: Option[Throwable]) extends LogMessage
  final case class WARN(message: String, throwOpt: Option[Throwable]) extends LogMessage
  final case class ERROR(message: String, throwOpt: Option[Throwable]) extends LogMessage

  def impl[F[_]: Sync](
      traceEnabled: Boolean = true,
      debugEnabled: Boolean = true,
      infoEnabled: Boolean = true,
      warnEnabled: Boolean = true,
      errorEnabled: Boolean = true
  ): TestingLogger[F] = {
    val ar = new AtomicReference(Vector.empty[LogMessage])
    def appendLogMessage(m: LogMessage): F[Unit] = Sync[F].delay {
      @tailrec
      def mod(): Unit = {
        val c = ar.get
        val u = c :+ m
        if (!ar.compareAndSet(c, u)) mod
        else ()
      }
      mod()
    }

    new TestingLogger[F] {
      def logged: F[Vector[LogMessage]] = Sync[F].delay(ar.get)

      def isTraceEnabled: F[Boolean] = Sync[F].pure(traceEnabled)
      def isDebugEnabled: F[Boolean] = Sync[F].pure(debugEnabled)
      def isInfoEnabled: F[Boolean] = Sync[F].pure(infoEnabled)
      def isWarnEnabled: F[Boolean] = Sync[F].pure(warnEnabled)
      def isErrorEnabled: F[Boolean] = Sync[F].pure(errorEnabled)

      def error(message: => String): F[Unit] =
        if (errorEnabled) appendLogMessage(ERROR(message, None)) else Sync[F].pure(())
      def error(t: Throwable)(message: => String): F[Unit] =
        if (errorEnabled) appendLogMessage(ERROR(message, t.some)) else Sync[F].pure(())

      def warn(message: => String): F[Unit] =
        if (warnEnabled) appendLogMessage(WARN(message, None)) else Sync[F].pure(())
      def warn(t: Throwable)(message: => String): F[Unit] =
        if (warnEnabled) appendLogMessage(WARN(message, t.some)) else Sync[F].pure(())

      def info(message: => String): F[Unit] =
        if (infoEnabled) appendLogMessage(INFO(message, None)) else Sync[F].pure(())
      def info(t: Throwable)(message: => String): F[Unit] =
        if (infoEnabled) appendLogMessage(INFO(message, t.some)) else Sync[F].pure(())

      def debug(message: => String): F[Unit] =
        if (debugEnabled) appendLogMessage(DEBUG(message, None)) else Sync[F].pure(())
      def debug(t: Throwable)(message: => String): F[Unit] =
        if (debugEnabled) appendLogMessage(DEBUG(message, t.some)) else Sync[F].pure(())

      def trace(message: => String): F[Unit] =
        if (traceEnabled) appendLogMessage(TRACE(message, None)) else Sync[F].pure(())
      def trace(t: Throwable)(message: => String): F[Unit] =
        if (traceEnabled) appendLogMessage(TRACE(message, t.some)) else Sync[F].pure(())
    }
  }

} 
Example 97
Source File: marshalling.scala    From hammock   with MIT License 5 votes vote down vote up
package hammock

import cats._
import cats.free._
import cats.effect.Sync

object marshalling {

  sealed trait MarshallF[A] {
    def dec: Decoder[A] // 
  }
  object MarshallF {
    def unmarshall[A: Decoder](entity: Entity): MarshallF[A] =
      Unmarshall(entity)

    private[marshalling] final case class Unmarshall[A](entity: Entity)(implicit D: Decoder[A]) extends MarshallF[A] {
      def dec = D
    }
  }

  object Ops {
    def unmarshall[A: Decoder](entity: Entity): Free[MarshallF, A] =
      Free.liftF(MarshallF.unmarshall(entity))
  }

  class MarshallC[F[_]](implicit I: MarshallF :<: F) {
    def unmarshall[A: Decoder](entity: Entity): Free[F, A] =
      Ops.unmarshall(entity).inject
  }

  implicit def marshallC[F[_]](implicit I: InjectK[MarshallF, F]): MarshallC[F] = new MarshallC[F]

  implicit def marshallNT[F[_]: Sync]: MarshallF ~> F = λ[MarshallF ~> F] {
    case [email protected](entity) => um.dec
        .decode(entity)
        .fold(Sync[F].raiseError, Sync[F].pure)
  }
} 
Example 98
Source File: package.scala    From hammock   with MIT License 5 votes vote down vote up
import cats._
import cats.data.{EitherK, NonEmptyList}
import cats.free.Free
import cats.effect.Sync
import contextual._

package object hammock {

  import hammock.marshalling._
  import hammock.InterpTrans

  type HammockF[A] = EitherK[HttpF, MarshallF, A]

  implicit class HttpRequestIOSyntax[A](fa: Free[HttpF, A]) {
    def exec[F[_]: Sync](implicit interp: InterpTrans[F]): F[A] =
      fa foldMap interp.trans
  }

  implicit class HammockFSyntax[A](fa: Free[HammockF, A]) {
    def exec[F[_]: Sync](implicit NT: HammockF ~> F): F[A] =
      fa foldMap NT
  }

  implicit class AsSyntaxOnHttpF[F[_], A](fa: Free[F, HttpResponse])(
      implicit
      H: InjectK[HttpF, F]) {
    def as[B](implicit D: Decoder[B], M: MarshallC[EitherK[F, MarshallF, *]]): Free[EitherK[F, MarshallF, *], B] =
      fa.inject[EitherK[F, MarshallF, *]] flatMap { response =>
        M.unmarshall(response.entity)
      }
  }

  implicit def hammockNT[F[_]: Sync](
      implicit H: InterpTrans[F],
      M: MarshallF ~> F
  ): HammockF ~> F = H.trans or M

  object UriContext extends Context

  object UriInterpolator extends Interpolator {
    type Output      = Uri
    type ContextType = UriContext.type
    type Input       = String
    def contextualize(interpolation: StaticInterpolation) = {
      interpolation.parts.foldLeft(List.empty[ContextType]) {
        case (contexts, Hole(_, _)) => UriContext :: contexts
        case (contexts, _)          => contexts
      }
    }

    def evaluate(interpolation: RuntimeInterpolation): Uri = {
      val substituted = interpolation.literals
        .zipAll(interpolation.substitutions, "", "")
        .flatMap(x => List(x._1, x._2))
        .mkString("")
      Uri.fromString(substituted).right.get
    }

  }

  implicit val embedString = UriInterpolator.embed[String](Case(UriContext, UriContext) { x =>
    x
  })

  
  implicit class UriQueryParamsBuilder(val self: NonEmptyList[(String, String)]) extends AnyVal {
    def &(param: (String, String)): NonEmptyList[(String, String)] = param :: self
  }
  implicit class UriQueryInitBuilder(val self: (String, String)) extends AnyVal {
    def &(param: (String, String)): NonEmptyList[(String, String)] = NonEmptyList(self, param :: Nil)
  }
} 
Example 99
Source File: codecs.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package examples.todolist
package http

import cats.Applicative
import cats.effect.Sync
import io.circe.generic.auto._
import org.http4s._
import org.http4s.circe._

object codecs {

  implicit def tagEncoder[F[_]: Applicative]: EntityEncoder[F, Tag] = jsonEncoderOf[F, Tag]
  implicit def tagDecoder[F[_]: Sync]: EntityDecoder[F, Tag]        = jsonOf[F, Tag]

  implicit def todoItemEncoder[F[_]: Applicative]: EntityEncoder[F, TodoItem] =
    jsonEncoderOf[F, TodoItem]
  implicit def todoItemDecoder[F[_]: Sync]: EntityDecoder[F, TodoItem] = jsonOf[F, TodoItem]

  implicit def todoListEncoder[F[_]: Applicative]: EntityEncoder[F, TodoList] =
    jsonEncoderOf[F, TodoList]
  implicit def todoListDecoder[F[_]: Sync]: EntityDecoder[F, TodoList] = jsonOf[F, TodoList]

  implicit def todoFormEncoder[F[_]: Applicative]: EntityEncoder[F, TodoForm] =
    jsonEncoderOf[F, TodoForm]
  implicit def todoFormDecoder[F[_]: Sync]: EntityDecoder[F, TodoForm] =
    jsonOf[F, TodoForm]
} 
Example 100
Source File: free.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package freestyle.free
package http

import _root_.hammock._
import cats.{MonadError, ~>}
import cats.effect.Sync
import cats.free.Free

object client {
  import freestyle.free._
  import freestyle.free.implicits._

  @free sealed trait HammockM {
    def options(uri: Uri, headers: Map[String, String]): FS[HttpResponse]
    def get(uri: Uri, headers: Map[String, String]): FS[HttpResponse]
    def head(uri: Uri, headers: Map[String, String]): FS[HttpResponse]
    def post(uri: Uri, headers: Map[String, String], body: Option[Entity]): FS[HttpResponse]
    def put(uri: Uri, headers: Map[String, String], body: Option[Entity]): FS[HttpResponse]
    def delete(uri: Uri, headers: Map[String, String]): FS[HttpResponse]
    def trace(uri: Uri, headers: Map[String, String]): FS[HttpResponse]
    def run[A](req: Free[HttpF, A]): FS[A]
  }

  trait Implicits {
    implicit def freeStyleHammockHandler[M[_] : MonadError[?[_], Throwable]](implicit I: InterpTrans[M], S: Sync[M]): HammockM.Handler[M] =
      new HammockM.Handler[M] {
        def options(uri: Uri, headers: Map[String, String]): M[HttpResponse] = Ops.options(uri, headers) foldMap I.trans
        def get(uri: Uri, headers: Map[String, String]): M[HttpResponse] = Ops.get(uri, headers) foldMap I.trans
        def head(uri: Uri, headers: Map[String, String]): M[HttpResponse] = Ops.head(uri, headers) foldMap I.trans
        def post(uri: Uri, headers: Map[String, String], body: Option[Entity]): M[HttpResponse] = Ops.post(uri, headers, body) foldMap I.trans
        def put(uri: Uri, headers: Map[String, String], body: Option[Entity]): M[HttpResponse] = Ops.put(uri, headers, body) foldMap I.trans
        def delete(uri: Uri, headers: Map[String, String]): M[HttpResponse] = Ops.delete(uri, headers) foldMap I.trans
        def trace(uri: Uri, headers: Map[String, String]): M[HttpResponse] = Ops.trace(uri, headers) foldMap I.trans
        def run[A](req: Free[HttpF, A]): M[A] = req foldMap I.trans
      }

    implicit def freeSLiftHammock[F[_]: HammockM]: FreeSLift[F, Free[HttpF, ?]] =
      new FreeSLift[F, Free[HttpF, ?]] {
        def liftFSPar[A](hio: Free[HttpF, A]): FreeS.Par[F, A] = HammockM[F].run(hio)
      }
  }

  object implicits extends Implicits
} 
Example 101
Source File: Config.scala    From scala-steward   with Apache License 2.0 5 votes vote down vote up
package org.scalasteward.core.application

import better.files._
import cats.effect.Sync
import org.http4s.Uri
import org.http4s.Uri.UserInfo
import org.scalasteward.core.application.Cli.EnvVar
import org.scalasteward.core.git.Author
import org.scalasteward.core.util
import org.scalasteward.core.vcs.data.AuthenticatedUser
import scala.concurrent.duration.FiniteDuration
import scala.sys.process.Process


final case class Config(
    workspace: File,
    reposFile: File,
    defaultRepoConfigFile: Option[File],
    gitAuthor: Author,
    vcsType: SupportedVCS,
    vcsApiHost: Uri,
    vcsLogin: String,
    gitAskPass: File,
    signCommits: Boolean,
    whitelistedDirectories: List[String],
    readOnlyDirectories: List[String],
    disableSandbox: Boolean,
    doNotFork: Boolean,
    ignoreOptsFiles: Boolean,
    envVars: List[EnvVar],
    processTimeout: FiniteDuration,
    scalafixMigrations: Option[File],
    groupMigrations: Option[File],
    cacheTtl: FiniteDuration,
    cacheMissDelay: FiniteDuration,
    bitbucketServerUseDefaultReviewers: Boolean
) {
  def vcsUser[F[_]](implicit F: Sync[F]): F[AuthenticatedUser] = {
    val urlWithUser = util.uri.withUserInfo.set(UserInfo(vcsLogin, None))(vcsApiHost).renderString
    val prompt = s"Password for '$urlWithUser': "
    F.delay {
      val password = Process(List(gitAskPass.pathAsString, prompt)).!!.trim
      AuthenticatedUser(vcsLogin, password)
    }
  }
}

object Config {
  def create[F[_]](args: Cli.Args)(implicit F: Sync[F]): F[Config] =
    F.delay {
      Config(
        workspace = args.workspace.toFile,
        reposFile = args.reposFile.toFile,
        defaultRepoConfigFile = args.defaultRepoConf.map(_.toFile),
        gitAuthor = Author(args.gitAuthorName, args.gitAuthorEmail),
        vcsType = args.vcsType,
        vcsApiHost = args.vcsApiHost,
        vcsLogin = args.vcsLogin,
        gitAskPass = args.gitAskPass.toFile,
        signCommits = args.signCommits,
        whitelistedDirectories = args.whitelist,
        readOnlyDirectories = args.readOnly,
        disableSandbox = args.disableSandbox,
        doNotFork = args.doNotFork,
        ignoreOptsFiles = args.ignoreOptsFiles,
        envVars = args.envVar,
        processTimeout = args.processTimeout,
        scalafixMigrations = args.scalafixMigrations.map(_.toFile),
        groupMigrations = args.groupMigrations.map(_.toFile),
        cacheTtl = args.cacheTtl,
        cacheMissDelay = args.cacheMissDelay,
        bitbucketServerUseDefaultReviewers = args.bitbucketServerUseDefaultReviewers
      )
    }
} 
Example 102
Source File: VCSSelection.scala    From scala-steward   with Apache License 2.0 5 votes vote down vote up
package org.scalasteward.core.vcs

import cats.effect.Sync
import org.scalasteward.core.application.Config
import org.scalasteward.core.application.SupportedVCS.{Bitbucket, BitbucketServer, GitHub, Gitlab}
import org.scalasteward.core.bitbucket.http4s.Http4sBitbucketApiAlg
import org.scalasteward.core.bitbucketserver.http4s.Http4sBitbucketServerApiAlg
import org.scalasteward.core.github.http4s.Http4sGitHubApiAlg
import org.scalasteward.core.gitlab.http4s.Http4sGitLabApiAlg
import org.scalasteward.core.util.HttpJsonClient
import org.scalasteward.core.vcs.data.AuthenticatedUser

class VCSSelection[F[_]: Sync](implicit client: HttpJsonClient[F], user: AuthenticatedUser) {
  private def github(config: Config): Http4sGitHubApiAlg[F] = {
    import org.scalasteward.core.github.http4s.authentication.addCredentials

    new Http4sGitHubApiAlg[F](config.vcsApiHost, _ => addCredentials(user))
  }
  private def gitlab(config: Config): Http4sGitLabApiAlg[F] = {
    import org.scalasteward.core.gitlab.http4s.authentication.addCredentials

    new Http4sGitLabApiAlg[F](config.vcsApiHost, user, _ => addCredentials(user), config.doNotFork)
  }

  private def bitbucket(config: Config): Http4sBitbucketApiAlg[F] = {
    import org.scalasteward.core.bitbucket.http4s.authentication.addCredentials

    new Http4sBitbucketApiAlg(config.vcsApiHost, user, _ => addCredentials(user), config.doNotFork)
  }

  private def bitbucketServer(config: Config): Http4sBitbucketServerApiAlg[F] = {
    import org.scalasteward.core.bitbucket.http4s.authentication.addCredentials

    new Http4sBitbucketServerApiAlg[F](
      config.vcsApiHost,
      _ => addCredentials(user),
      config.bitbucketServerUseDefaultReviewers
    )
  }

  def getAlg(config: Config): VCSApiAlg[F] =
    config.vcsType match {
      case GitHub          => github(config)
      case Gitlab          => gitlab(config)
      case Bitbucket       => bitbucket(config)
      case BitbucketServer => bitbucketServer(config)
    }
} 
Example 103
Source File: Http4sBitbucketApiAlg.scala    From scala-steward   with Apache License 2.0 5 votes vote down vote up
package org.scalasteward.core.bitbucket.http4s

import cats.effect.Sync
import cats.implicits._
import org.http4s.{Request, Status, Uri}
import org.scalasteward.core.bitbucket.Url
import org.scalasteward.core.bitbucket.http4s.json._
import org.scalasteward.core.git.Branch
import org.scalasteward.core.util.{HttpJsonClient, UnexpectedResponse}
import org.scalasteward.core.vcs.VCSApiAlg
import org.scalasteward.core.vcs.data._

class Http4sBitbucketApiAlg[F[_]: Sync](
    bitbucketApiHost: Uri,
    user: AuthenticatedUser,
    modify: Repo => Request[F] => F[Request[F]],
    doNotFork: Boolean
)(implicit client: HttpJsonClient[F])
    extends VCSApiAlg[F] {
  private val url = new Url(bitbucketApiHost)

  override def createFork(repo: Repo): F[RepoOut] =
    for {
      fork <- client.post[RepositoryResponse](url.forks(repo), modify(repo)).recoverWith {
        case UnexpectedResponse(_, _, _, Status.BadRequest, _) =>
          client.get(url.repo(repo.copy(owner = user.login)), modify(repo))
      }
      maybeParent <-
        fork.parent
          .map(n => client.get[RepositoryResponse](url.repo(n), modify(n)))
          .sequence[F, RepositoryResponse]
    } yield mapToRepoOut(fork, maybeParent)

  private def mapToRepoOut(
      repo: RepositoryResponse,
      maybeParent: Option[RepositoryResponse]
  ): RepoOut =
    RepoOut(
      repo.name,
      repo.owner,
      maybeParent.map(p => mapToRepoOut(p, None)),
      repo.httpsCloneUrl,
      repo.mainBranch
    )

  override def createPullRequest(repo: Repo, data: NewPullRequestData): F[PullRequestOut] = {
    val sourceBranchOwner = if (doNotFork) repo.owner else user.login

    val payload = CreatePullRequestRequest(
      data.title,
      Branch(data.head),
      Repo(sourceBranchOwner, repo.repo),
      data.base,
      data.body
    )
    client.postWithBody(url.pullRequests(repo), payload, modify(repo))
  }

  override def getBranch(repo: Repo, branch: Branch): F[BranchOut] =
    client.get(url.branch(repo, branch), modify(repo))

  override def getRepo(repo: Repo): F[RepoOut] =
    for {
      repo <- client.get[RepositoryResponse](url.repo(repo), modify(repo))
      maybeParent <-
        repo.parent
          .map(n => client.get[RepositoryResponse](url.repo(n), modify(n)))
          .sequence[F, RepositoryResponse]
    } yield mapToRepoOut(repo, maybeParent)

  override def listPullRequests(repo: Repo, head: String, base: Branch): F[List[PullRequestOut]] =
    client
      .get[Page[PullRequestOut]](url.listPullRequests(repo, head), modify(repo))
      .map(_.values)
} 
Example 104
Source File: DateTimeAlg.scala    From scala-steward   with Apache License 2.0 5 votes vote down vote up
package org.scalasteward.core.util

import cats.effect.Sync
import cats.implicits._
import cats.{Functor, Monad}
import java.util.concurrent.TimeUnit
import scala.concurrent.duration.FiniteDuration

trait DateTimeAlg[F[_]] {
  def currentTimeMillis: F[Long]

  final def currentTimestamp(implicit F: Functor[F]): F[Timestamp] =
    currentTimeMillis.map(Timestamp.apply)

  final def timed[A](fa: F[A])(implicit F: Monad[F]): F[(A, FiniteDuration)] =
    for {
      start <- currentTimeMillis
      a <- fa
      end <- currentTimeMillis
      duration = FiniteDuration(end - start, TimeUnit.MILLISECONDS)
    } yield (a, duration)
}

object DateTimeAlg {
  def create[F[_]](implicit F: Sync[F]): DateTimeAlg[F] =
    new DateTimeAlg[F] {
      override def currentTimeMillis: F[Long] =
        F.delay(System.currentTimeMillis())
    }
} 
Example 105
Source File: HttpJsonClient.scala    From scala-steward   with Apache License 2.0 5 votes vote down vote up
package org.scalasteward.core.util

import cats.effect.Sync
import cats.implicits._
import io.circe.{Decoder, Encoder}
import org.http4s.Method.{GET, POST}
import org.http4s.circe.{jsonEncoderOf, jsonOf}
import org.http4s.client.Client
import org.http4s._
import scala.util.control.NoStackTrace

final class HttpJsonClient[F[_]: Sync](implicit
    client: Client[F]
) {
  type ModReq = Request[F] => F[Request[F]]

  def get[A: Decoder](uri: Uri, modify: ModReq): F[A] =
    request[A](GET, uri, modify)

  def post[A: Decoder](uri: Uri, modify: ModReq): F[A] =
    request[A](POST, uri, modify)

  def postWithBody[A: Decoder, B: Encoder](uri: Uri, body: B, modify: ModReq): F[A] =
    post[A](uri, modify.compose(_.withEntity(body)(jsonEncoderOf[F, B])))

  private def request[A: Decoder](method: Method, uri: Uri, modify: ModReq): F[A] =
    client.expectOr[A](modify(Request[F](method, uri)))(resp =>
      toUnexpectedResponse(uri, method, resp)
    )(jsonOf[F, A].transform(_.leftMap(failure => JsonParseError(uri, method, failure))))

  private def toUnexpectedResponse(
      uri: Uri,
      method: Method,
      response: Response[F]
  ): F[Throwable] = {
    val body = response.body.through(fs2.text.utf8Decode).compile.string
    body.map(UnexpectedResponse(uri, method, response.headers, response.status, _))
  }
}

final case class JsonParseError(
    uri: Uri,
    method: Method,
    underlying: DecodeFailure
) extends DecodeFailure {
  val message = s"uri: $uri\nmethod: $method\nmessage: ${underlying.message}"
  override def cause: Option[Throwable] = underlying.some
  override def toHttpResponse[F[_]](httpVersion: HttpVersion): Response[F] =
    underlying.toHttpResponse(httpVersion)
}

final case class UnexpectedResponse(
    uri: Uri,
    method: Method,
    headers: Headers,
    status: Status,
    body: String
) extends RuntimeException
    with NoStackTrace {
  override def getMessage: String =
    s"uri: $uri\nmethod: $method\nstatus: $status\nheaders: $headers\nbody: $body"
} 
Example 106
Source File: MillAlg.scala    From scala-steward   with Apache License 2.0 5 votes vote down vote up
package org.scalasteward.core.buildtool.mill

import cats.implicits._
import cats.effect.Sync
import org.scalasteward.core.BuildInfo
import org.scalasteward.core.buildtool.BuildToolAlg
import org.scalasteward.core.data.Scope
import org.scalasteward.core.data.Scope.Dependencies
import org.scalasteward.core.io.{FileAlg, ProcessAlg, WorkspaceAlg}
import org.scalasteward.core.scalafix.Migration
import org.scalasteward.core.util.Nel
import org.scalasteward.core.vcs.data.Repo

trait MillAlg[F[_]] extends BuildToolAlg[F]

object MillAlg {
  private val content =
    s"""|import coursierapi.MavenRepository
       |
       |interp.repositories() ++= Seq(
       |  MavenRepository.of("https://oss.sonatype.org/content/repositories/snapshots/")
       |)
       |interp.load.ivy("${BuildInfo.organization}" %% "${BuildInfo.millPluginModuleName}" % "${BuildInfo.version}")
       |""".stripMargin

  def create[F[_]](implicit
      fileAlg: FileAlg[F],
      processAlg: ProcessAlg[F],
      workspaceAlg: WorkspaceAlg[F],
      F: Sync[F]
  ): MillAlg[F] =
    new MillAlg[F] {
      override def containsBuild(repo: Repo): F[Boolean] =
        workspaceAlg.repoDir(repo).flatMap(repoDir => fileAlg.isRegularFile(repoDir / "build.sc"))

      override def getDependencies(repo: Repo): F[List[Dependencies]] =
        for {
          repoDir <- workspaceAlg.repoDir(repo)
          predef = repoDir / "scala-steward.sc"
          _ <- fileAlg.writeFile(predef, content)
          millcmd = if ((repoDir / "mill").exists) (repoDir / "mill").toString() else "mill"
          extracted <- processAlg.exec(
            Nel(
              millcmd,
              List(
                "-i",
                "-p",
                predef.toString(),
                "show",
                s"${BuildInfo.millPluginModuleRootPkg}.StewardPlugin/extractDeps"
              )
            ),
            repoDir
          )
          parsed <- F.fromEither(
            parser.parseModules(extracted.dropWhile(!_.startsWith("{")).mkString("\n"))
          )
          _ <- fileAlg.deleteForce(predef)

        } yield parsed.map(module => Scope(module.dependencies, module.repositories))

      override def runMigrations(repo: Repo, migrations: Nel[Migration]): F[Unit] = F.unit
    }
} 
Example 107
Source File: MigrationAlg.scala    From scala-steward   with Apache License 2.0 5 votes vote down vote up
package org.scalasteward.core.scalafix

import better.files.File
import cats.data.OptionT
import cats.effect.Sync
import cats.implicits._
import io.circe.config.parser.decode
import org.scalasteward.core.data.{Update, Version}
import org.scalasteward.core.io.FileAlg
import org.scalasteward.core.util.{ApplicativeThrowable, MonadThrowable}

trait MigrationAlg {
  def findMigrations(update: Update): List[Migration]
}

object MigrationAlg {
  def create[F[_]](extraMigrations: Option[File])(implicit
      fileAlg: FileAlg[F],
      F: Sync[F]
  ): F[MigrationAlg] =
    loadMigrations(extraMigrations).map { migrations =>
      new MigrationAlg {
        override def findMigrations(update: Update): List[Migration] =
          findMigrationsImpl(migrations, update)
      }
    }

  def loadMigrations[F[_]](
      extraMigrations: Option[File]
  )(implicit fileAlg: FileAlg[F], F: MonadThrowable[F]): F[List[Migration]] =
    for {
      default <-
        fileAlg
          .readResource("scalafix-migrations.conf")
          .flatMap(decodeMigrations[F](_, "default"))
      maybeExtra <- OptionT(extraMigrations.flatTraverse(fileAlg.readFile))
        .semiflatMap(decodeMigrations[F](_, "extra"))
        .value
      migrations = maybeExtra match {
        case Some(extra) if extra.disableDefaults => extra.migrations
        case Some(extra)                          => default.migrations ++ extra.migrations
        case None                                 => default.migrations
      }
    } yield migrations

  private def decodeMigrations[F[_]](content: String, tpe: String)(implicit
      F: ApplicativeThrowable[F]
  ): F[ScalafixMigrations] =
    F.fromEither(decode[ScalafixMigrations](content))
      .adaptErr(new Throwable(s"Failed to load $tpe Scalafix migrations", _))

  private def findMigrationsImpl(
      givenMigrations: List[Migration],
      update: Update
  ): List[Migration] =
    givenMigrations.filter { migration =>
      update.groupId === migration.groupId &&
      migration.artifactIds.exists(re =>
        update.artifactIds.exists(artifactId => re.r.findFirstIn(artifactId.name).isDefined)
      ) &&
      Version(update.currentVersion) < migration.newVersion &&
      Version(update.newerVersions.head) >= migration.newVersion
    }
} 
Example 108
Source File: MockContext.scala    From scala-steward   with Apache License 2.0 5 votes vote down vote up
package org.scalasteward.core.mock

import better.files.File
import cats.Parallel
import cats.effect.Sync
import org.http4s.Uri
import org.scalasteward.core.TestInstances.ioContextShift
import org.scalasteward.core.application.Cli.EnvVar
import org.scalasteward.core.application.{Config, SupportedVCS}
import org.scalasteward.core.buildtool.BuildToolDispatcher
import org.scalasteward.core.buildtool.maven.MavenAlg
import org.scalasteward.core.buildtool.mill.MillAlg
import org.scalasteward.core.buildtool.sbt.SbtAlg
import org.scalasteward.core.coursier.{CoursierAlg, VersionsCache}
import org.scalasteward.core.edit.EditAlg
import org.scalasteward.core.git.{Author, GitAlg}
import org.scalasteward.core.io.{MockFileAlg, MockProcessAlg, MockWorkspaceAlg}
import org.scalasteward.core.nurture.PullRequestRepository
import org.scalasteward.core.persistence.JsonKeyValueStore
import org.scalasteward.core.repocache.RepoCacheRepository
import org.scalasteward.core.repoconfig.RepoConfigAlg
import org.scalasteward.core.scalafix.MigrationAlg
import org.scalasteward.core.scalafmt.ScalafmtAlg
import org.scalasteward.core.update.{FilterAlg, GroupMigrations, PruningAlg, UpdateAlg}
import org.scalasteward.core.util.uri._
import org.scalasteward.core.util.{BracketThrowable, DateTimeAlg}
import org.scalasteward.core.vcs.VCSRepoAlg
import org.scalasteward.core.vcs.data.AuthenticatedUser

import scala.concurrent.duration._

object MockContext {
  implicit val config: Config = Config(
    workspace = File.temp / "ws",
    reposFile = File.temp / "repos.md",
    defaultRepoConfigFile = Some(File.temp / "default.scala-steward.conf"),
    gitAuthor = Author("Bot Doe", "[email protected]"),
    vcsType = SupportedVCS.GitHub,
    vcsApiHost = Uri(),
    vcsLogin = "bot-doe",
    gitAskPass = File.temp / "askpass.sh",
    signCommits = false,
    whitelistedDirectories = Nil,
    readOnlyDirectories = Nil,
    disableSandbox = false,
    doNotFork = false,
    ignoreOptsFiles = false,
    envVars = List(
      EnvVar("TEST_VAR", "GREAT"),
      EnvVar("ANOTHER_TEST_VAR", "ALSO_GREAT")
    ),
    processTimeout = 10.minutes,
    scalafixMigrations = None,
    groupMigrations = None,
    cacheTtl = 1.hour,
    cacheMissDelay = 0.milliseconds,
    bitbucketServerUseDefaultReviewers = false
  )

  implicit val mockEffBracketThrowable: BracketThrowable[MockEff] = Sync[MockEff]
  implicit val mockEffParallel: Parallel[MockEff] = Parallel.identity

  implicit val fileAlg: MockFileAlg = new MockFileAlg
  implicit val mockLogger: MockLogger = new MockLogger
  implicit val processAlg: MockProcessAlg = new MockProcessAlg
  implicit val workspaceAlg: MockWorkspaceAlg = new MockWorkspaceAlg

  implicit val coursierAlg: CoursierAlg[MockEff] = CoursierAlg.create
  implicit val dateTimeAlg: DateTimeAlg[MockEff] = DateTimeAlg.create
  implicit val gitAlg: GitAlg[MockEff] = GitAlg.create
  implicit val user: AuthenticatedUser = AuthenticatedUser("scala-steward", "token")
  implicit val vcsRepoAlg: VCSRepoAlg[MockEff] = VCSRepoAlg.create(config, gitAlg)
  implicit val scalafmtAlg: ScalafmtAlg[MockEff] = ScalafmtAlg.create
  implicit val migrationAlg: MigrationAlg =
    MigrationAlg.create[MockEff](config.scalafixMigrations).runA(MockState.empty).unsafeRunSync()
  implicit val cacheRepository: RepoCacheRepository[MockEff] =
    new RepoCacheRepository[MockEff](new JsonKeyValueStore("repo_cache", "1"))
  implicit val filterAlg: FilterAlg[MockEff] = new FilterAlg[MockEff]
  implicit val versionsCache: VersionsCache[MockEff] =
    new VersionsCache[MockEff](config.cacheTtl, new JsonKeyValueStore("versions", "1"))
  implicit val groupMigrations: GroupMigrations =
    GroupMigrations.create[MockEff].runA(MockState.empty).unsafeRunSync()
  implicit val updateAlg: UpdateAlg[MockEff] = new UpdateAlg[MockEff]
  implicit val mavenAlg: MavenAlg[MockEff] = MavenAlg.create
  implicit val sbtAlg: SbtAlg[MockEff] = SbtAlg.create
  implicit val millAlg: MillAlg[MockEff] = MillAlg.create
  implicit val buildToolDispatcher: BuildToolDispatcher[MockEff] = BuildToolDispatcher.create
  implicit val editAlg: EditAlg[MockEff] = new EditAlg[MockEff]
  implicit val repoConfigAlg: RepoConfigAlg[MockEff] = new RepoConfigAlg[MockEff]
  implicit val pullRequestRepository: PullRequestRepository[MockEff] =
    new PullRequestRepository[MockEff](new JsonKeyValueStore("pull_requests", "1"))
  implicit val pruningAlg: PruningAlg[MockEff] = new PruningAlg[MockEff]
} 
Example 109
Source File: loggerTest.scala    From scala-steward   with Apache License 2.0 5 votes vote down vote up
package org.scalasteward.core.util

import cats.effect.Sync
import org.scalasteward.core.mock.MockContext._
import org.scalasteward.core.mock.{MockEff, MockState}
import org.scalasteward.core.util.logger.LoggerOps
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers

class loggerTest extends AnyFunSuite with Matchers {
  test("attemptLog_") {
    final case class Err(msg: String) extends Throwable(msg)
    val err = Err("hmm?")
    val state = mockLogger
      .attemptLog_("run")(Sync[MockEff].raiseError(err))
      .runS(MockState.empty)
      .unsafeRunSync()

    state.logs shouldBe Vector((None, "run"), (Some(err), "run failed"))
  }

  test("infoTimed") {
    val state = mockLogger
      .infoTimed(_ => "timed")(mockLogger.info("inner"))
      .runS(MockState.empty)
      .unsafeRunSync()

    state.logs shouldBe Vector((None, "inner"), (None, "timed"))
  }
} 
Example 110
Source File: ElasticClient.scala    From fs2-elastic   with MIT License 5 votes vote down vote up
package com.alessandromarrella.fs2_elastic
import org.elasticsearch.client.{
  RestClient,
  RestClientBuilder,
  RestHighLevelClient
}
import fs2._
import cats.effect.Sync
import org.apache.http.HttpHost

object Client {

  def fromHosts[F[_]](hosts: HttpHost*)(
      implicit F: Sync[F]): Stream[F, RestHighLevelClient] =
    Stream.bracket(
      F.delay(new RestHighLevelClient(RestClient.builder(hosts: _*))))(
      c => F.delay(c.close())
    )

  def fromClientBuilder[F[_]](restClientBuilder: RestClientBuilder)(
      implicit F: Sync[F]): Stream[F, RestHighLevelClient] =
    Stream.bracket(F.delay(new RestHighLevelClient(restClientBuilder)))(
      c => F.delay(c.close())
    )
} 
Example 111
Source File: CassandraSync.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal.eventual.cassandra

import cats.arrow.FunctionK
import cats.effect.concurrent.Semaphore
import cats.effect.{Concurrent, Sync, Timer}
import cats.implicits._
import cats.~>
import com.evolutiongaming.cassandra
import com.evolutiongaming.cassandra.sync.AutoCreate
import com.evolutiongaming.kafka.journal.Origin

trait CassandraSync[F[_]] {
  def apply[A](fa: F[A]): F[A]
}

object CassandraSync {

  def empty[F[_]]: CassandraSync[F] = new CassandraSync[F] {
    def apply[A](fa: F[A]) = fa
  }


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


  def apply[F[_] : Sync : Timer : CassandraSession](
    config: SchemaConfig,
    origin: Option[Origin],
  ): CassandraSync[F] = {

    val keyspace = config.keyspace
    val autoCreate = if (keyspace.autoCreate) AutoCreate.Table else AutoCreate.None
    apply(
      keyspace = keyspace.name,
      table = config.locksTable,
      autoCreate = autoCreate,
      metadata = origin.map(_.value))
  }

  def apply[F[_] : Sync : Timer : CassandraSession](
    keyspace: String,
    table: String,
    autoCreate: AutoCreate,
    metadata: Option[String],
  ): CassandraSync[F] = {

    new CassandraSync[F] {

      def apply[A](fa: F[A]) = {

        val cassandraSync = cassandra.sync.CassandraSync.of[F](
          session = CassandraSession[F].unsafe,
          keyspace = keyspace,
          table = table,
          autoCreate = autoCreate)

        for {
          cassandraSync <- cassandraSync
          result        <- cassandraSync(id = "kafka-journal", metadata = metadata)(fa)
        } yield result
      }
    }
  }

  def of[F[_] : Concurrent : Timer : CassandraSession](
    config: SchemaConfig,
    origin: Option[Origin]
  ): F[CassandraSync[F]] = {

    for {
      semaphore <- Semaphore[F](1)
    } yield {
      val cassandraSync = apply[F](config, origin)
      val serial = new (F ~> F) {
        def apply[A](fa: F[A]) = semaphore.withPermit(fa)
      }
      cassandraSync.mapK(serial, FunctionK.id)
    }
  }


  implicit class CassandraSyncOps[F[_]](val self: CassandraSync[F]) extends AnyVal {

    def mapK[G[_]](fg: F ~> G, gf: G ~> F): CassandraSync[G] = new CassandraSync[G] {

      def apply[A](fa: G[A]) = fg(self(gf(fa)))
    }
  }
} 
Example 112
Source File: ResultSet.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal.eventual.cassandra

import cats.effect.{Concurrent, Sync}
import cats.effect.implicits._
import cats.implicits._
import com.datastax.driver.core.{Row, ResultSet => ResultSetJ}
import com.evolutiongaming.scassandra.util.FromGFuture
import com.evolutiongaming.sstream.Stream
import com.evolutiongaming.sstream.FoldWhile._


object ResultSet {

  def apply[F[_] : Concurrent : FromGFuture](resultSet: ResultSetJ): Stream[F, Row] = {

    val iterator = resultSet.iterator()

    val fetch = FromGFuture[F].apply { resultSet.fetchMoreResults() }.void

    val fetched = Sync[F].delay { resultSet.isFullyFetched }

    val next = Sync[F].delay { List.fill(resultSet.getAvailableWithoutFetching)(iterator.next()) }

    apply[F, Row](fetch, fetched, next)
  }

  def apply[F[_] : Concurrent, A](
    fetch: F[Unit],
    fetched: F[Boolean],
    next: F[List[A]]
  ): Stream[F, A] = new Stream[F, A] {

    def foldWhileM[L, R](l: L)(f: (L, A) => F[Either[L, R]]) = {

      l.tailRecM[F, Either[L, R]] { l =>

        def apply(rows: List[A]) = {
          for {
            result <- rows.foldWhileM(l)(f)
          } yield {
            result.asRight[L]
          }
        }

        def fetchAndApply(rows: List[A]) = {
          for {
            fetching <- fetch.start
            result   <- rows.foldWhileM(l)(f)
            result   <- result match {
              case l: Left[L, R]  => fetching.join as l.rightCast[Either[L, R]]
              case r: Right[L, R] => r.leftCast[L].asRight[L].pure[F]
            }
          } yield result
        }

        for {
          fetched <- fetched
          rows    <- next
          result  <- if (fetched) apply(rows) else fetchAndApply(rows)
        } yield result
      }
    }
  }
} 
Example 113
Source File: TopicCommit.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal.replicator

import java.time.Instant

import cats.Applicative
import cats.data.{NonEmptyMap => Nem}
import cats.effect.concurrent.Ref
import cats.effect.{Clock, Sync}
import cats.implicits._
import com.evolutiongaming.catshelper.ClockHelper._
import com.evolutiongaming.catshelper.DataHelper._
import com.evolutiongaming.kafka.journal.util.TemporalHelper._
import com.evolutiongaming.kafka.journal.KafkaConsumer
import com.evolutiongaming.skafka._

import scala.collection.immutable.SortedMap
import scala.concurrent.duration._

trait TopicCommit[F[_]] {

  def apply(offsets: Nem[Partition, Offset]): F[Unit]
}

object TopicCommit {

  def empty[F[_] : Applicative]: TopicCommit[F] = (_: Nem[Partition, Offset]) => ().pure[F]

  def apply[F[_]](
    topic: Topic,
    metadata: String,
    consumer: KafkaConsumer[F, _, _],
  ): TopicCommit[F] = {
    offsets: Nem[Partition, Offset] => {
      val offsets1 = offsets.mapKV { (partition, offset) =>
        val offset1 = OffsetAndMetadata(offset, metadata)
        val partition1 = TopicPartition(topic, partition)
        (partition1, offset1)
      }
      consumer.commit(offsets1)
    }
  }

  def delayed[F[_] : Sync : Clock](
    delay: FiniteDuration,
    commit: TopicCommit[F]
  ): F[TopicCommit[F]] = {

    case class State(until: Instant, offsets: SortedMap[Partition, Offset] = SortedMap.empty)

    for {
      timestamp <- Clock[F].instant
      stateRef  <- Ref[F].of(State(timestamp + delay))
    } yield {
      new TopicCommit[F] {
        def apply(offsets: Nem[Partition, Offset]) = {

          def apply(state: State, timestamp: Instant) = {
            val offsets1 = state.offsets ++ offsets.toSortedMap
            if (state.until <= timestamp) {
              offsets1
                .toNem
                .foldMapM { offsets => commit(offsets) }
                .as(State(timestamp + delay))
            } else {
              state
                .copy(offsets = offsets1)
                .pure[F]
            }
          }

          for {
            timestamp <- Clock[F].instant
            state     <- stateRef.get
            state     <- apply(state, timestamp)
            _         <- stateRef.set(state)
          } yield {}
        }
      }
    }
  }
} 
Example 114
Source File: LogFromAkka.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal

import akka.event.LoggingAdapter
import cats.effect.Sync
import com.evolutiongaming.catshelper.Log

object LogFromAkka {

  def apply[F[_] : Sync](log: LoggingAdapter): Log[F] = new Log[F] {

    def debug(msg: => String) = {
      Sync[F].delay {
        if (log.isDebugEnabled) log.debug(msg)
      }
    }

    def info(msg: => String) = {
      Sync[F].delay {
        if (log.isInfoEnabled) log.info(msg)
      }
    }

    def warn(msg: => String) = {
      Sync[F].delay {
        if (log.isWarningEnabled) log.warning(msg)
      }
    }

    def warn(msg: => String, cause: Throwable) = {
      Sync[F].delay {
        if (log.isWarningEnabled) log.warning(s"$msg: $cause")
      }
    }

    def error(msg: => String) = {
      Sync[F].delay {
        if (log.isErrorEnabled) log.error(msg)
      }
    }

    def error(msg: => String, cause: Throwable) = {
      Sync[F].delay {
        if (log.isErrorEnabled) log.error(cause, msg)
      }
    }
  }
} 
Example 115
Source File: ThreadFactoryOf.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal.execution

import java.util.concurrent.{ThreadFactory, Executors => ExecutorsJ}

import cats.effect.Sync
import cats.implicits._

object ThreadFactoryOf {

  def apply[F[_] : Sync](
    prefix: String,
    uncaughtExceptionHandler: Thread.UncaughtExceptionHandler = UncaughtExceptionHandler.default
  ): F[ThreadFactory] = {

    for {
      factory <- Sync[F].delay { ExecutorsJ.defaultThreadFactory() }
    } yield {
      new ThreadFactory {
        def newThread(runnable: Runnable) = {
          val thread = factory.newThread(runnable)
          val threadId = thread.getId
          thread.setName(s"$prefix-$threadId")
          thread.setUncaughtExceptionHandler(uncaughtExceptionHandler)
          thread
        }
      }
    }
  }
} 
Example 116
Source File: ThreadPoolOf.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal.execution

import java.util.concurrent.{SynchronousQueue, ThreadFactory, ThreadPoolExecutor}

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

import scala.concurrent.duration._

object ThreadPoolOf {

  def apply[F[_] : Sync](
    minSize: Int,
    maxSize: Int,
    threadFactory: ThreadFactory,
    keepAlive: FiniteDuration = 1.minute,
  ): Resource[F, ThreadPoolExecutor] = {

    val result = for {
      result <- Sync[F].delay {
        new ThreadPoolExecutor(
          minSize,
          maxSize,
          keepAlive.length,
          keepAlive.unit,
          new SynchronousQueue[Runnable],
          threadFactory)
      }
    } yield {
      val release = Sync[F].delay { result.shutdown() }
      (result, release)
    }

    Resource(result)
  }
} 
Example 117
Source File: ForkJoinPoolOf.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal.execution

import java.util.concurrent.ForkJoinPool
import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory

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


object ForkJoinPoolOf {

  def apply[F[_] : Sync](
    name: String,
    parallelism: Int
  ): Resource[F, ForkJoinPool] = {

    val threadFactory = ForkJoinPool.defaultForkJoinWorkerThreadFactory.withPrefix(name)

    val threadPool = Sync[F].delay {
      new ForkJoinPool(
        parallelism,
        threadFactory,
        UncaughtExceptionHandler.default,
        true)
    }

    val result = for {
      threadPool <- threadPool
    } yield {
      val release = Sync[F].delay { threadPool.shutdown() }
      (threadPool, release)
    }

    Resource(result)
  }


  implicit class ForkJoinWorkerThreadFactoryOps(val self: ForkJoinWorkerThreadFactory) extends AnyVal {

    def withPrefix(prefix: String): ForkJoinWorkerThreadFactory = new ForkJoinWorkerThreadFactory {

      def newThread(pool: ForkJoinPool) = {
        val thread = self.newThread(pool)
        val threadId = thread.getId
        thread.setName(s"$prefix-$threadId")
        thread
      }
    }
  }
} 
Example 118
Source File: ScheduledExecutorServiceOf.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal.execution

import java.util.concurrent.{ScheduledExecutorService, ThreadFactory, Executors => ExecutorsJ}

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


object ScheduledExecutorServiceOf {

  def apply[F[_] : Sync](
    parallelism: Int,
    threadFactory: ThreadFactory
  ): Resource[F, ScheduledExecutorService] = {

    val result = for {
      threadPool <- Sync[F].delay { ExecutorsJ.newScheduledThreadPool(parallelism, threadFactory) }
    } yield {
      val release = Sync[F].delay { threadPool.shutdown() }
      (threadPool, release)
    }
    Resource(result)
  }
} 
Example 119
Source File: HostName.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal

import cats.effect.Sync


final case class HostName(value: String) {

  override def toString: String = value
}

object HostName {

  def of[F[_] : Sync](): F[Option[HostName]] = {
    Sync[F].delay {
      for {
        a <- com.evolutiongaming.hostname.HostName()
      } yield {
        HostName(a)
      }
    }
  }
} 
Example 120
Source File: ResourceRef.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal.util

import cats.effect.concurrent.Ref
import cats.effect.implicits._
import cats.effect.{Resource, Sync}
import cats.implicits._

import scala.util.control.NoStackTrace

trait ResourceRef[F[_], A] {

  def get: F[A]

  def set(a: A, release: F[Unit]): F[Unit]

  def set(a: Resource[F, A]): F[Unit]
}

object ResourceRef {

  def of[F[_] : Sync, A](resource: Resource[F, A]): Resource[F, ResourceRef[F, A]] = {

    case class State(a: A, release: F[Unit])

    Resource
      .make {
        for {
          ab           <- resource.allocated
          (a, release)  = ab
          ref          <- Ref[F].of(State(a, release).some)
        } yield ref
      } { ref =>
        ref
          .getAndSet(none)
          .flatMap { _.foldMapM { _.release } }
      }
      .map { ref =>
        new ResourceRef[F, A] {

          def get = {
            ref
              .get
              .flatMap {
                case Some(state) => state.a.pure[F]
                case None        => ResourceReleasedError.raiseError[F, A]
              }
          }

          def set(a: A, release: F[Unit]) = {
            ref
              .modify {
                case Some(state) => (State(a, release).some, state.release )
                case None        => (none, ResourceReleasedError.raiseError[F, Unit])
              }
              .flatten
              .uncancelable
          }

          def set(a: Resource[F, A]) = {
            a
              .allocated
              .flatMap { case (a, release) => set(a, release) }
          }
        }
      }
  }
}

case object ResourceReleasedError extends RuntimeException("Resource released") with NoStackTrace 
Example 121
Source File: ActorSystemOf.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal.util

import akka.actor.ActorSystem
import cats.effect.{Resource, Sync}
import cats.implicits._
import com.evolutiongaming.catshelper.FromFuture
import com.typesafe.config.Config

object ActorSystemOf {

  def apply[F[_] : Sync : FromFuture](
    name: String,
    config: Option[Config] = None): Resource[F, ActorSystem] = {

    val system = Sync[F].delay { config.fold(ActorSystem(name)) { config => ActorSystem(name, config) } }

    for {
      system <- Resource.liftF(system)
      result <- apply(system)
    } yield result
  }


  def apply[F[_] : Sync : FromFuture](system: ActorSystem): Resource[F, ActorSystem] = {
    val release = FromFuture[F].apply { system.terminate() }.void
    val result = (system, release).pure[F]
    Resource(result)
  }
} 
Example 122
Source File: Executors.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal.util

import java.util.concurrent.ScheduledExecutorService

import cats.effect.{Resource, Sync}
import com.evolutiongaming.catshelper.Runtime
import com.evolutiongaming.kafka.journal.execution.{ForkJoinPoolOf, ScheduledExecutorServiceOf, ThreadFactoryOf, ThreadPoolOf}

import scala.concurrent.{ExecutionContext, ExecutionContextExecutorService}

object Executors {

  def blocking[F[_] : Sync](
    name: String,
  ): Resource[F, ExecutionContextExecutorService] = {
    for {
      threadFactory <- Resource.liftF(ThreadFactoryOf[F](name))
      threadPool    <- ThreadPoolOf[F](2, Int.MaxValue, threadFactory)
    } yield {
      ExecutionContext.fromExecutorService(threadPool)
    }
  }


  def nonBlocking[F[_] : Sync](
    name: String,
  ): Resource[F, ExecutionContextExecutorService] = {
    for {
      cores        <- Resource.liftF(Runtime[F].availableCores)
      parallelism   = cores + 1
      forkJoinPool <- ForkJoinPoolOf[F](name, parallelism)
    } yield {
      ExecutionContext.fromExecutorService(forkJoinPool)
    }
  }


  def scheduled[F[_] : Sync](
    name: String,
    parallelism: Int
  ): Resource[F, ScheduledExecutorService] = {
    for {
      threadFactory <- Resource.liftF(ThreadFactoryOf[F](name))
      result        <- ScheduledExecutorServiceOf[F](parallelism, threadFactory)
    } yield result
  }
} 
Example 123
Source File: LogOfFromAkka.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal

import akka.actor.ActorSystem
import akka.event.LogSource
import cats.effect.Sync
import cats.implicits._
import com.evolutiongaming.catshelper.LogOf

object LogOfFromAkka {

  def apply[F[_] : Sync](system: ActorSystem): LogOf[F] = {

    def log[A: LogSource](source: A) = {
      for {
        log <- Sync[F].delay { akka.event.Logging(system, source) }
      } yield {
        LogFromAkka[F](log)
      }
    }

    new LogOf[F] {

      def apply(source: String) = log(source)

      def apply(source: Class[_]) = log(source)
    }
  }
} 
Example 124
Source File: TestSync.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package com.evolutiongaming.kafka.journal.util

import cats.Monad
import cats.effect.{ExitCase, Sync}

import scala.util.control.NonFatal

object TestSync {

  def apply[F[_]](implicit F: Monad[F]): Sync[F] = new Sync[F] {

    def suspend[A](thunk: => F[A]) = thunk

    def bracketCase[A, B](acquire: F[A])(use: A => F[B])(release: (A, ExitCase[Throwable]) => F[Unit]) = {
      flatMap(acquire) { a =>
        try {
          val b = use(a)
          try release(a, ExitCase.Completed) catch { case NonFatal(_) => }
          b
        } catch {
          case NonFatal(e) =>
            release(a, ExitCase.Error(e))
            raiseError(e)
        }
      }
    }

    def raiseError[A](e: Throwable) = throw e

    def handleErrorWith[A](fa: F[A])(f: Throwable => F[A]) = try fa catch { case NonFatal(e) => f(e) }

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

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

    def pure[A](a: A) = F.pure(a)
  }
} 
Example 125
Source File: SerializedMsgSerializer.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package akka.persistence.kafka.journal

import akka.actor.ActorSystem
import cats.effect.Sync
import cats.implicits._
import cats.~>
import com.evolutiongaming.serialization.{SerializedMsg, SerializedMsgConverter, SerializedMsgExt}

trait SerializedMsgSerializer[F[_]] {

  def toMsg(a: AnyRef): F[SerializedMsg]

  def fromMsg(a: SerializedMsg): F[AnyRef]
}

object SerializedMsgSerializer {

  def of[F[_] : Sync](actorSystem: ActorSystem): F[SerializedMsgSerializer[F]] = {
    for {
      converter <- Sync[F].delay { SerializedMsgExt(actorSystem) }
    } yield {
      apply(converter)
    }
  }

  def apply[F[_] : Sync](converter: SerializedMsgConverter): SerializedMsgSerializer[F] = {

    new SerializedMsgSerializer[F] {

      def toMsg(a: AnyRef) = {
        Sync[F].delay { converter.toMsg(a) }
      }

      def fromMsg(a: SerializedMsg) = {
        for {
          a <- Sync[F].delay { converter.fromMsg(a) }
          a <- Sync[F].fromTry(a)
        } yield a
      }
    }
  }


  implicit class SerializedMsgSerializerOps[F[_]](val self: SerializedMsgSerializer[F]) extends AnyVal {

    def mapK[G[_]](f: F ~> G): SerializedMsgSerializer[G] = new SerializedMsgSerializer[G] {

      def toMsg(a: AnyRef) = f(self.toMsg(a))

      def fromMsg(a: SerializedMsg) = f(self.fromMsg(a))
    }
  }
} 
Example 126
Source File: EventSerializerSpec.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package akka.persistence.kafka.journal

import java.io.FileOutputStream

import akka.persistence.PersistentRepr
import akka.persistence.serialization.Snapshot
import cats.effect.{IO, Sync}
import com.evolutiongaming.kafka.journal.FromBytes.implicits._
import com.evolutiongaming.kafka.journal.IOSuite._
import com.evolutiongaming.kafka.journal._
import com.evolutiongaming.kafka.journal.util.CatsHelper._
import org.scalatest.funsuite.AsyncFunSuite
import org.scalatest.matchers.should.Matchers
import play.api.libs.json.JsString
import scodec.bits.ByteVector
import TestJsonCodec.instance
import cats.implicits._

import scala.util.Try

class EventSerializerSpec extends AsyncFunSuite with ActorSuite with Matchers {

  for {
    (name, payloadType, payload) <- List(
      ("PersistentRepr.bin",       PayloadType.Binary, Snapshot("binary")),
      ("PersistentRepr.text.json", PayloadType.Json, "text"),
      ("PersistentRepr.json",      PayloadType.Json, JsString("json")))
  } {

    test(s"toEvent & toPersistentRepr, payload: $payload") {
      val persistenceId = "persistenceId"
      val persistentRepr = PersistentRepr(
        payload = payload,
        sequenceNr = 1,
        persistenceId = persistenceId,
        manifest = "manifest",
        writerUuid = "writerUuid")

      val fa = for {
        serializer <- EventSerializer.of[IO](actorSystem)
        event      <- serializer.toEvent(persistentRepr)
        actual     <- serializer.toPersistentRepr(persistenceId, event)
        _          <- Sync[IO].delay { actual shouldEqual persistentRepr }
        payload    <- event.payload.getOrError[IO]("Event.payload is not defined")
        _           = payload.payloadType shouldEqual payloadType
        
        bytes      <- ByteVectorOf[IO](getClass, name)
      } yield {
        payload match {
          case payload: Payload.Binary => payload.value shouldEqual bytes
          case payload: Payload.Text   => payload.value shouldEqual bytes.fromBytes[Try, String].get
          case payload: Payload.Json   => payload.value shouldEqual JsonCodec.summon[Try].decode.fromBytes(bytes).get
        }
      }

      fa.run()
    }
  }

  def writeToFile[F[_] : Sync](bytes: ByteVector, path: String): F[Unit] = {
    Sync[F].delay {
      val os = new FileOutputStream(path)
      os.write(bytes.toArray)
      os.close()
    }
  }
} 
Example 127
Source File: ActorSystemRefTest.scala    From kafka-journal   with MIT License 5 votes vote down vote up
package akka.persistence.kafka.journal

import cats.arrow.FunctionK
import cats.effect.{IO, Sync}
import cats.implicits._
import com.evolutiongaming.kafka.journal.ActorSuite
import com.evolutiongaming.kafka.journal.IOSuite._
import org.scalatest.funsuite.AsyncFunSuite
import org.scalatest.matchers.should.Matchers

class ActorSystemRefTest extends AsyncFunSuite with ActorSuite with Matchers {
  import ActorSystemRefTest._

  test("Extension") {
    val result = for {
      ref <- Sync[IO].delay { Extension(actorSystem) }
      ref <- ref.fromFuture[IO].mapK(FunctionK.id).pure[IO]
      a   <- ref.get.start
      _   <- ref.set(0)
      a   <- a.join
      _    = a shouldEqual 0
      a   <- ref.get
      _    = a shouldEqual 0
      a   <- ref.set(0).attempt
      _    = a.isLeft shouldEqual true
    } yield {}
    result.run()
  }
}

object ActorSystemRefTest {
  object Extension extends ActorSystemRef.ExtensionId[Int]
} 
Example 128
Source File: data.scala    From redis4cats   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.redis4cats

import cats.effect.Sync
import cats.syntax.functor._
import dev.profunktor.redis4cats.JavaConversions._
import io.lettuce.core.{ ReadFrom => JReadFrom }
import io.lettuce.core.codec.{ ByteArrayCodec, CipherCodec, CompressionCodec, RedisCodec => JRedisCodec, StringCodec }
import io.lettuce.core.{ KeyScanCursor => JKeyScanCursor }
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec

object data {

  final case class RedisChannel[K](underlying: K) extends AnyVal

  final case class RedisCodec[K, V](underlying: JRedisCodec[K, V]) extends AnyVal
  final case class NodeId(value: String) extends AnyVal

  final case class KeyScanCursor[K](underlying: JKeyScanCursor[K]) extends AnyVal {
    def keys: List[K]  = underlying.getKeys.asScala.toList
    def cursor: String = underlying.getCursor
  }

  object RedisCodec {
    val Ascii: RedisCodec[String, String]           = RedisCodec(StringCodec.ASCII)
    val Utf8: RedisCodec[String, String]            = RedisCodec(StringCodec.UTF8)
    val Bytes: RedisCodec[Array[Byte], Array[Byte]] = RedisCodec(ByteArrayCodec.INSTANCE)

    
    def decryptSupplier[F[_]: Sync](key: SecretKeySpec): F[CipherCodec.CipherSupplier] =
      cipherSupplier[F](key, Cipher.DECRYPT_MODE)

    private def cipherSupplier[F[_]: Sync](key: SecretKeySpec, mode: Int): F[CipherCodec.CipherSupplier] = {
      val mkCipher =
        F.delay {
          val cipher: Cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
          cipher.init(mode, key)
          cipher
        }

      mkCipher.map { cipher =>
        new CipherCodec.CipherSupplier {
          override def get(kd: CipherCodec.KeyDescriptor): Cipher = cipher
        }
      }
    }

  }

  object ReadFrom {
    val Master           = JReadFrom.MASTER
    val MasterPreferred  = JReadFrom.MASTER_PREFERRED
    val Nearest          = JReadFrom.NEAREST
    val Replica          = JReadFrom.REPLICA
    val ReplicaPreferred = JReadFrom.REPLICA_PREFERRED
  }

} 
Example 129
Source File: Log.scala    From redis4cats   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.redis4cats.effect

import cats.Applicative
import cats.effect.Sync


trait Log[F[_]] {
  def debug(msg: => String): F[Unit]
  def error(msg: => String): F[Unit]
  def info(msg: => String): F[Unit]
}

object Log {
  def apply[F[_]](implicit ev: Log[F]): Log[F] = ev

  object NoOp {
    implicit def instance[F[_]: Applicative]: Log[F] =
      new Log[F] {
        def debug(msg: => String): F[Unit] = F.unit
        def error(msg: => String): F[Unit] = F.unit
        def info(msg: => String): F[Unit]  = F.unit
      }
  }

  object Stdout {
    implicit def instance[F[_]: Sync]: Log[F] =
      new Log[F] {
        def debug(msg: => String): F[Unit] =
          F.delay(Console.out.println(msg))
        def error(msg: => String): F[Unit] =
          F.delay(Console.err.println(msg))
        def info(msg: => String): F[Unit] =
          F.delay(Console.out.println(msg))
      }
  }

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

import cats.effect.{Sync, Timer}
import cats.syntax.apply._
import dev.profunktor.fs2rabbit.effects.Log
import fs2.Stream

import scala.concurrent.duration._
import scala.util.control.NonFatal


object ResilientStream {

  def runF[F[_]: Log: Sync: Timer](program: F[Unit], retry: FiniteDuration = 5.seconds): F[Unit] =
    run(Stream.eval(program), retry)

  def run[F[_]: Log: Sync: Timer](
      program: Stream[F, Unit],
      retry: FiniteDuration = 5.seconds
  ): F[Unit] =
    loop(program, retry, 1).compile.drain

  private def loop[F[_]: Log: Sync: Timer](
      program: Stream[F, Unit],
      retry: FiniteDuration,
      count: Int
  ): Stream[F, Unit] =
    program.handleErrorWith {
      case NonFatal(err) =>
        Stream.eval(Log[F].error(err.getMessage) *> Log[F].info(s"Restarting in ${retry.toSeconds * count}...")) >>
          loop[F](Stream.sleep(retry) >> program, retry, count + 1)
    }

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

import cats.effect.Sync
import cats.syntax.functor._
import dev.profunktor.fs2rabbit.config.deletion
import dev.profunktor.fs2rabbit.config.deletion.{DeletionExchangeConfig, DeletionQueueConfig}
import dev.profunktor.fs2rabbit.effects.BoolValue.syntax._
import dev.profunktor.fs2rabbit.model.AMQPChannel

object Deletion {
  def make[F[_]: Sync]: Deletion[F] = new Deletion[F] {
    override def deleteQueue(channel: AMQPChannel, config: DeletionQueueConfig): F[Unit] =
      Sync[F].delay {
        channel.value.queueDelete(
          config.queueName.value,
          config.ifUnused.isTrue,
          config.ifEmpty.isTrue
        )
      }.void

    override def deleteQueueNoWait(channel: AMQPChannel, config: DeletionQueueConfig): F[Unit] =
      Sync[F].delay {
        channel.value.queueDeleteNoWait(
          config.queueName.value,
          config.ifUnused.isTrue,
          config.ifEmpty.isTrue
        )
      }.void

    override def deleteExchange(
        channel: AMQPChannel,
        config: deletion.DeletionExchangeConfig
    ): F[Unit] =
      Sync[F].delay {
        channel.value.exchangeDelete(config.exchangeName.value, config.ifUnused.isTrue)
      }.void

    override def deleteExchangeNoWait(
        channel: AMQPChannel,
        config: deletion.DeletionExchangeConfig
    ): F[Unit] =
      Sync[F].delay {
        channel.value.exchangeDeleteNoWait(
          config.exchangeName.value,
          config.ifUnused.isTrue
        )
      }.void
  }
}

trait Deletion[F[_]] {
  def deleteQueue(channel: AMQPChannel, config: DeletionQueueConfig): F[Unit]
  def deleteQueueNoWait(channel: AMQPChannel, config: DeletionQueueConfig): F[Unit]
  def deleteExchange(channel: AMQPChannel, config: DeletionExchangeConfig): F[Unit]
  def deleteExchangeNoWait(channel: AMQPChannel, config: DeletionExchangeConfig): F[Unit]
} 
Example 132
Source File: Connection.scala    From fs2-rabbit   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.fs2rabbit.algebra

import cats.data.NonEmptyList
import cats.effect.{Resource, Sync}
import cats.implicits._
import com.rabbitmq.client.{Address, ConnectionFactory, DefaultSaslConfig, SaslConfig}
import dev.profunktor.fs2rabbit.config.Fs2RabbitConfig
import dev.profunktor.fs2rabbit.effects.Log
import dev.profunktor.fs2rabbit.javaConversion._
import dev.profunktor.fs2rabbit.model.{AMQPChannel, AMQPConnection, RabbitChannel, RabbitConnection}
import javax.net.ssl.SSLContext

object ConnectionResource {
  type ConnectionResource[F[_]] = Connection[Resource[F, ?]]
  def make[F[_]: Sync: Log](
      conf: Fs2RabbitConfig,
      sslCtx: Option[SSLContext] = None,
      // Unlike SSLContext, SaslConfig is not optional because it is always set
      // by the underlying Java library, even if the user doesn't set it.
      saslConf: SaslConfig = DefaultSaslConfig.PLAIN
  ): F[Connection[Resource[F, ?]]] =
    Sync[F].delay {
      new Connection[Resource[F, ?]] {

        private[fs2rabbit] def mkConnectionFactory: F[(ConnectionFactory, NonEmptyList[Address])] =
          Sync[F].delay {
            val factory   = new ConnectionFactory()
            val firstNode = conf.nodes.head
            factory.setHost(firstNode.host)
            factory.setPort(firstNode.port)
            factory.setVirtualHost(conf.virtualHost)
            factory.setConnectionTimeout(conf.connectionTimeout)
            factory.setAutomaticRecoveryEnabled(conf.automaticRecovery)
            if (conf.ssl) {
              sslCtx.fold(factory.useSslProtocol())(factory.useSslProtocol)
            }
            factory.setSaslConfig(saslConf)
            conf.username.foreach(factory.setUsername)
            conf.password.foreach(factory.setPassword)
            val addresses = conf.nodes.map(node => new Address(node.host, node.port))
            (factory, addresses)
          }

        private[fs2rabbit] def acquireChannel(connection: AMQPConnection): F[AMQPChannel] =
          Sync[F]
            .delay(connection.value.createChannel)
            .flatTap(c => Log[F].info(s"Acquired channel: $c"))
            .map(RabbitChannel)

        private[fs2rabbit] val acquireConnection: F[AMQPConnection] =
          mkConnectionFactory.flatMap {
            case (factory, addresses) =>
              Sync[F]
                .delay(factory.newConnection(addresses.toList.asJava))
                .flatTap(c => Log[F].info(s"Acquired connection: $c"))
                .map(RabbitConnection)
          }

        override def createConnection: Resource[F, AMQPConnection] =
          Resource.make(acquireConnection) {
            case RabbitConnection(conn) =>
              Log[F].info(s"Releasing connection: $conn previously acquired.") *>
                Sync[F].delay {
                  if (conn.isOpen) conn.close()
                }
          }

        override def createChannel(connection: AMQPConnection): Resource[F, AMQPChannel] =
          Resource.make(acquireChannel(connection)) {
            case RabbitChannel(channel) =>
              Sync[F].delay {
                if (channel.isOpen) channel.close()
              }
          }
      }
    }
}

trait Connection[F[_]] {
  def createConnection: F[AMQPConnection]
  def createChannel(connection: AMQPConnection): F[AMQPChannel]
} 
Example 133
Source File: Publish.scala    From fs2-rabbit   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.fs2rabbit.algebra

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

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

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

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

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

          channel.value.addReturnListener(returnListener)
        }.void

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

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

import cats.effect.Sync
import cats.syntax.functor._
import dev.profunktor.fs2rabbit.arguments._
import dev.profunktor.fs2rabbit.config.declaration.{DeclarationExchangeConfig, DeclarationQueueConfig}
import dev.profunktor.fs2rabbit.effects.BoolValue.syntax._
import dev.profunktor.fs2rabbit.model.{AMQPChannel, ExchangeName, QueueName}

object Declaration {
  def make[F[_]: Sync]: Declaration[F] = new Declaration[F] {
    override def declareExchange(channel: AMQPChannel, config: DeclarationExchangeConfig): F[Unit] =
      Sync[F].delay {
        channel.value.exchangeDeclare(
          config.exchangeName.value,
          config.exchangeType.toString.toLowerCase,
          config.durable.isTrue,
          config.autoDelete.isTrue,
          config.internal.isTrue,
          config.arguments
        )
      }.void

    override def declareExchangeNoWait(
        channel: AMQPChannel,
        config: DeclarationExchangeConfig
    ): F[Unit] =
      Sync[F].delay {
        channel.value.exchangeDeclareNoWait(
          config.exchangeName.value,
          config.exchangeType.toString.toLowerCase,
          config.durable.isTrue,
          config.autoDelete.isTrue,
          config.internal.isTrue,
          config.arguments
        )
      }.void

    override def declareExchangePassive(channel: AMQPChannel, exchangeName: ExchangeName): F[Unit] =
      Sync[F].delay {
        channel.value.exchangeDeclarePassive(exchangeName.value)
      }.void

    override def declareQueue(channel: AMQPChannel): F[QueueName] =
      Sync[F].delay {
        QueueName(channel.value.queueDeclare().getQueue)
      }

    override def declareQueue(channel: AMQPChannel, config: DeclarationQueueConfig): F[Unit] =
      Sync[F].delay {
        channel.value.queueDeclare(
          config.queueName.value,
          config.durable.isTrue,
          config.exclusive.isTrue,
          config.autoDelete.isTrue,
          config.arguments
        )
      }.void

    override def declareQueueNoWait(channel: AMQPChannel, config: DeclarationQueueConfig): F[Unit] =
      Sync[F].delay {
        channel.value.queueDeclareNoWait(
          config.queueName.value,
          config.durable.isTrue,
          config.exclusive.isTrue,
          config.autoDelete.isTrue,
          config.arguments
        )
      }.void

    override def declareQueuePassive(channel: AMQPChannel, queueName: QueueName): F[Unit] =
      Sync[F].delay {
        channel.value.queueDeclarePassive(queueName.value)
      }.void
  }
}

trait Declaration[F[_]] {
  def declareExchange(channel: AMQPChannel, exchangeConfig: DeclarationExchangeConfig): F[Unit]
  def declareExchangeNoWait(value: AMQPChannel, exchangeConfig: DeclarationExchangeConfig): F[Unit]
  def declareExchangePassive(channel: AMQPChannel, exchangeName: ExchangeName): F[Unit]
  def declareQueue(channel: AMQPChannel): F[QueueName]
  def declareQueue(channel: AMQPChannel, queueConfig: DeclarationQueueConfig): F[Unit]
  def declareQueueNoWait(channel: AMQPChannel, queueConfig: DeclarationQueueConfig): F[Unit]
  def declareQueuePassive(channel: AMQPChannel, queueName: QueueName): F[Unit]
} 
Example 135
Source File: AckingProgram.scala    From fs2-rabbit   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.fs2rabbit.program

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

import cats.effect.Sync
import cats.syntax.functor._
import fs2.{Pipe, Stream}

trait StreamEval[F[_]] {
  def pure[A](body: A): Stream[F, A]
  def evalF[A](body: => A): Stream[F, A]
  def evalDiscard[A](body: => A): Stream[F, Unit]
  def liftSink[A](f: A => F[Unit]): Pipe[F, A, Unit]
  def liftPipe[A, B](f: A => F[B]): Pipe[F, A, B]
}

object StreamEval {

  implicit def syncStreamEvalInstance[F[_]: Sync]: StreamEval[F] =
    new StreamEval[F] {
      override def pure[A](body: A): Stream[F, A] =
        Stream(body).covary[F]

      override def evalF[A](body: => A): Stream[F, A] =
        Stream.eval(Sync[F].delay(body))

      override def evalDiscard[A](body: => A): Stream[F, Unit] =
        Stream.eval(Sync[F].delay(body).void)

      override def liftSink[A](f: A => F[Unit]): Pipe[F, A, Unit] =
        liftPipe[A, Unit](f)

      override def liftPipe[A, B](f: A => F[B]): Pipe[F, A, B] =
        _.evalMap(f)
    }

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

import cats.effect.Sync
import org.slf4j.LoggerFactory

trait Log[F[_]] {
  def info(value: => String): F[Unit]
  def error(value: => String): F[Unit]
}

object Log {
  private[fs2rabbit] val logger = LoggerFactory.getLogger(this.getClass)

  def apply[F[_]](implicit ev: Log[F]): Log[F] = ev

  implicit def syncLogInstance[F[_]](implicit F: Sync[F]): Log[F] =
    new Log[F] {
      override def error(value: => String): F[Unit] = F.delay(logger.error(value))
      override def info(value: => String): F[Unit]  = F.delay(logger.info(value))
    }
} 
Example 139
Source File: Markdown.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.convert.flexmark

import java.io.{InputStream, InputStreamReader}
import java.nio.charset.Charset
import java.util

import scala.util.Try

import cats.effect.Sync
import cats.implicits._
import fs2.Stream

import docspell.common._

import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension
import com.vladsch.flexmark.ext.tables.TablesExtension
import com.vladsch.flexmark.html.HtmlRenderer
import com.vladsch.flexmark.parser.Parser
import com.vladsch.flexmark.util.data.{DataKey, MutableDataSet}

object Markdown {

  def toHtml(
      is: InputStream,
      cfg: MarkdownConfig,
      cs: Charset
  ): Either[Throwable, String] = {
    val p = createParser()
    val r = createRenderer()
    Try {
      val reader = new InputStreamReader(is, cs)
      val doc    = p.parseReader(reader)
      wrapHtml(r.render(doc), cfg)
    }.toEither
  }

  def toHtml(md: String, cfg: MarkdownConfig): String = {
    val p   = createParser()
    val r   = createRenderer()
    val doc = p.parse(md)
    wrapHtml(r.render(doc), cfg)
  }

  def toHtml[F[_]: Sync](
      data: Stream[F, Byte],
      cfg: MarkdownConfig,
      cs: Charset
  ): F[String] =
    data.through(Binary.decode(cs)).compile.foldMonoid.map(str => toHtml(str, cfg))

  private def wrapHtml(body: String, cfg: MarkdownConfig): String =
    s"""<!DOCTYPE html>
       |<html>
       |<head>
       |<meta charset="utf-8"/>
       |<style>
       |${cfg.internalCss}
       |</style>
       |</head>
       |<body>
       |$body
       |</body>
       |</html>
       |""".stripMargin

  private def createParser(): Parser = {
    val opts = new MutableDataSet()
    opts.set(
      Parser.EXTENSIONS.asInstanceOf[DataKey[util.Collection[_]]],
      util.Arrays.asList(TablesExtension.create(), StrikethroughExtension.create())
    );

    Parser.builder(opts).build()
  }

  private def createRenderer(): HtmlRenderer = {
    val opts = new MutableDataSet()
    HtmlRenderer.builder(opts).build()
  }
} 
Example 140
Source File: InfoRoutes.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.joex.routes

import cats.effect.Sync

import docspell.joex.BuildInfo
import docspell.joexapi.model.VersionInfo

import org.http4s.HttpRoutes
import org.http4s.circe.CirceEntityEncoder._
import org.http4s.dsl.Http4sDsl

object InfoRoutes {

  def apply[F[_]: Sync](): HttpRoutes[F] = {
    val dsl = new Http4sDsl[F] {}
    import dsl._
    HttpRoutes.of[F] {
      case GET -> (Root / "version") =>
        Ok(
          VersionInfo(
            BuildInfo.version,
            BuildInfo.builtAtMillis,
            BuildInfo.builtAtString,
            BuildInfo.gitHeadCommit.getOrElse(""),
            BuildInfo.gitDescribedVersion.getOrElse("")
          )
        )
    }
  }
} 
Example 141
Source File: EvalProposals.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.joex.process

import java.time.{LocalDate, Period}

import cats.effect.Sync
import cats.implicits._

import docspell.common._
import docspell.joex.scheduler.Task
import docspell.store.records.RAttachmentMeta


object EvalProposals {

  def apply[F[_]: Sync](data: ItemData): Task[F, ProcessItemArgs, ItemData] =
    Task { _ =>
      Timestamp
        .current[F]
        .map { now =>
          val metas = data.metas.map(calcCandidateWeight(now.toUtcDate))
          data.copy(metas = metas)
        }
    }

  def calcCandidateWeight(now: LocalDate)(rm: RAttachmentMeta): RAttachmentMeta = {
    val list = rm.proposals.change(mp => mp.addWeights(weight(rm, mp, now)))
    rm.copy(proposals = list.sortByWeights)
  }

  def weight(rm: RAttachmentMeta, mp: MetaProposal, ref: LocalDate)(
      cand: MetaProposal.Candidate
  ): Double =
    mp.proposalType match {
      case MetaProposalType.DueDate =>
        //for due dates, sort earliest on top
        MetaProposal
          .parseDate(cand)
          .map { ld =>
            val p = Period.between(ref, ld)
            // conversion only for sorting
            val d = p.getYears * 365 + p.getMonths * 31 + p.getDays
            d.toDouble
          }
          .getOrElse(2000.0)
      case _ =>
        val textLen  = rm.content.map(_.length).getOrElse(0)
        val tagCount = cand.origin.size.toDouble
        val pos      = cand.origin.map(_.startPosition).min
        val words    = cand.origin.map(_.label.split(' ').length).max.toDouble
        val nerFac =
          cand.origin.map(label => nerTagFactor(label.tag, mp.proposalType)).min
        (1 / words) * (1 / tagCount) * positionWeight(pos, textLen) * nerFac
    }

  def positionWeight(pos: Int, total: Int): Double =
    if (total <= 0) 1
    else {
      val p = math.abs(pos.toDouble / total.toDouble)
      if (p < 0.7) p / 2
      else p
    }

  def nerTagFactor(tag: NerTag, mt: MetaProposalType): Double =
    tag match {
      case NerTag.Date     => 1.0
      case NerTag.Email    => 0.5
      case NerTag.Location => 1.0
      case NerTag.Misc     => 1.0
      case NerTag.Organization =>
        if (mt == MetaProposalType.CorrOrg) 0.8
        else 1.0
      case NerTag.Person =>
        if (
          mt == MetaProposalType.CorrPerson ||
          mt == MetaProposalType.ConcPerson
        ) 0.8
        else 1.0
      case NerTag.Website => 0.5
    }
} 
Example 142
Source File: TextAnalysis.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.joex.process

import cats.effect.Sync
import cats.implicits._

import docspell.analysis.{TextAnalyser, TextAnalysisConfig}
import docspell.common._
import docspell.joex.process.ItemData.AttachmentDates
import docspell.joex.scheduler.Task
import docspell.store.records.RAttachmentMeta

object TextAnalysis {

  def apply[F[_]: Sync](
      cfg: TextAnalysisConfig
  )(item: ItemData): Task[F, ProcessItemArgs, ItemData] =
    Task { ctx =>
      TextAnalyser.create[F](cfg).use { analyser =>
        for {
          _ <- ctx.logger.info("Starting text analysis")
          s <- Duration.stopTime[F]
          t <-
            item.metas.toList
              .traverse(
                annotateAttachment[F](ctx.args.meta.language, ctx.logger, analyser)
              )
          _ <- ctx.logger.debug(s"Storing tags: ${t.map(_._1.copy(content = None))}")
          _ <- t.traverse(m =>
            ctx.store.transact(RAttachmentMeta.updateLabels(m._1.id, m._1.nerlabels))
          )
          e <- s
          _ <- ctx.logger.info(s"Text-Analysis finished in ${e.formatExact}")
          v = t.toVector
        } yield item.copy(metas = v.map(_._1), dateLabels = v.map(_._2))
      }
    }

  def annotateAttachment[F[_]: Sync](
      lang: Language,
      logger: Logger[F],
      analyser: TextAnalyser[F]
  )(rm: RAttachmentMeta): F[(RAttachmentMeta, AttachmentDates)] =
    for {
      labels <- analyser.annotate(logger, lang, rm.content.getOrElse(""))
    } yield (rm.copy(nerlabels = labels.all.toList), AttachmentDates(rm, labels.dates))

} 
Example 143
Source File: TestTasks.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.joex.process

import cats.effect.Sync
import cats.implicits._

import docspell.common.ProcessItemArgs
import docspell.common.syntax.all._
import docspell.joex.scheduler.Task

import org.log4s._

object TestTasks {
  private[this] val logger = getLogger

  def success[F[_]]: Task[F, ProcessItemArgs, Unit] =
    Task(ctx => ctx.logger.info(s"Running task now: ${ctx.args}"))

  def failing[F[_]: Sync]: Task[F, ProcessItemArgs, Unit] =
    Task { ctx =>
      ctx.logger
        .info(s"Failing the task run :(")
        .map(_ => sys.error("Oh, cannot extract gold from this document"))
    }

  def longRunning[F[_]: Sync]: Task[F, ProcessItemArgs, Unit] =
    Task { ctx =>
      logger.fwarn(s"${Thread.currentThread()} From executing long running task") >>
        ctx.logger.info(s"${Thread.currentThread()} Running task now: ${ctx.args}") >>
        sleep(2400) >>
        ctx.logger.debug("doing things") >>
        sleep(2400) >>
        ctx.logger.debug("doing more things") >>
        sleep(2400) >>
        ctx.logger.info("doing more things")
    }

  private def sleep[F[_]: Sync](ms: Long): F[Unit] =
    Sync[F].delay(Thread.sleep(ms))
} 
Example 144
Source File: SaveProposals.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.joex.process

import cats.effect.Sync
import cats.implicits._

import docspell.common._
import docspell.joex.scheduler.Task
import docspell.store.records._


object SaveProposals {

  def apply[F[_]: Sync](data: ItemData): Task[F, ProcessItemArgs, ItemData] =
    Task { ctx =>
      ctx.logger.info("Storing proposals") *>
        data.metas
          .traverse(rm =>
            ctx.logger.debug(s"Storing attachment proposals: ${rm.proposals}") *>
              ctx.store.transact(RAttachmentMeta.updateProposals(rm.id, rm.proposals))
          )
          .map(_ => data)
    }
} 
Example 145
Source File: QueueLogger.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.joex.scheduler

import cats.effect.{Concurrent, Sync}
import cats.implicits._
import fs2.concurrent.Queue

import docspell.common._

object QueueLogger {

  def create[F[_]: Sync](
      jobId: Ident,
      jobInfo: String,
      q: Queue[F, LogEvent]
  ): Logger[F] =
    new Logger[F] {
      def trace(msg: => String): F[Unit] =
        LogEvent.create[F](jobId, jobInfo, LogLevel.Debug, msg).flatMap(q.enqueue1)

      def debug(msg: => String): F[Unit] =
        LogEvent.create[F](jobId, jobInfo, LogLevel.Debug, msg).flatMap(q.enqueue1)

      def info(msg: => String): F[Unit] =
        LogEvent.create[F](jobId, jobInfo, LogLevel.Info, msg).flatMap(q.enqueue1)

      def warn(msg: => String): F[Unit] =
        LogEvent.create[F](jobId, jobInfo, LogLevel.Warn, msg).flatMap(q.enqueue1)

      def error(ex: Throwable)(msg: => String): F[Unit] =
        LogEvent
          .create[F](jobId, jobInfo, LogLevel.Error, msg)
          .map(le => le.copy(ex = Some(ex)))
          .flatMap(q.enqueue1)

      def error(msg: => String): F[Unit] =
        LogEvent.create[F](jobId, jobInfo, LogLevel.Error, msg).flatMap(q.enqueue1)
    }

  def apply[F[_]: Concurrent](
      jobId: Ident,
      jobInfo: String,
      bufferSize: Int,
      sink: LogSink[F]
  ): F[Logger[F]] =
    for {
      q <- Queue.circularBuffer[F, LogEvent](bufferSize)
      log = create(jobId, jobInfo, q)
      _ <- Concurrent[F].start(q.dequeue.through(sink.receive).compile.drain)
    } yield log

} 
Example 146
Source File: LogSink.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.joex.scheduler

import cats.effect.{Concurrent, Sync}
import cats.implicits._
import fs2.{Pipe, Stream}

import docspell.common._
import docspell.common.syntax.all._
import docspell.store.Store
import docspell.store.records.RJobLog

import org.log4s.{LogLevel => _, _}

trait LogSink[F[_]] {

  def receive: Pipe[F, LogEvent, Unit]

}

object LogSink {
  private[this] val logger = getLogger

  def apply[F[_]](sink: Pipe[F, LogEvent, Unit]): LogSink[F] =
    new LogSink[F] {
      val receive = sink
    }

  def logInternal[F[_]: Sync](e: LogEvent): F[Unit] =
    e.level match {
      case LogLevel.Info =>
        logger.finfo(e.logLine)
      case LogLevel.Debug =>
        logger.fdebug(e.logLine)
      case LogLevel.Warn =>
        logger.fwarn(e.logLine)
      case LogLevel.Error =>
        e.ex match {
          case Some(exc) =>
            logger.ferror(exc)(e.logLine)
          case None =>
            logger.ferror(e.logLine)
        }
    }

  def printer[F[_]: Sync]: LogSink[F] =
    LogSink(_.evalMap(e => logInternal(e)))

  def db[F[_]: Sync](store: Store[F]): LogSink[F] =
    LogSink(
      _.evalMap(ev =>
        for {
          id <- Ident.randomId[F]
          joblog = RJobLog(
            id,
            ev.jobId,
            ev.level,
            ev.time,
            ev.msg + ev.ex.map(th => ": " + th.getMessage).getOrElse("")
          )
          _ <- logInternal(ev)
          _ <- store.transact(RJobLog.insert(joblog))
        } yield ()
      )
    )

  def dbAndLog[F[_]: Concurrent](store: Store[F]): LogSink[F] = {
    val s: Stream[F, Pipe[F, LogEvent, Unit]] =
      Stream.emits(Seq(printer[F].receive, db[F](store).receive))
    LogSink(Pipe.join(s))
  }
} 
Example 147
Source File: LogEvent.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.joex.scheduler

import cats.effect.Sync
import cats.implicits._

import docspell.common._

case class LogEvent(
    jobId: Ident,
    jobInfo: String,
    time: Timestamp,
    level: LogLevel,
    msg: String,
    ex: Option[Throwable] = None
) {

  def logLine: String =
    s">>> ${time.asString} $level $jobInfo: $msg"

}

object LogEvent {

  def create[F[_]: Sync](
      jobId: Ident,
      jobInfo: String,
      level: LogLevel,
      msg: String
  ): F[LogEvent] =
    Timestamp.current[F].map(now => LogEvent(jobId, jobInfo, now, level, msg))

} 
Example 148
Source File: JobTask.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.joex.scheduler

import cats.effect.Sync
import cats.implicits._

import docspell.common.Ident
import docspell.common.syntax.all._

import io.circe.Decoder


case class JobTask[F[_]](
    name: Ident,
    task: Task[F, String, Unit],
    onCancel: Task[F, String, Unit]
)

object JobTask {

  def json[F[_]: Sync, A](
      name: Ident,
      task: Task[F, A, Unit],
      onCancel: Task[F, A, Unit]
  )(implicit
      D: Decoder[A]
  ): JobTask[F] = {
    val convert: String => F[A] =
      str =>
        str.parseJsonAs[A] match {
          case Right(a) => a.pure[F]
          case Left(ex) =>
            Sync[F].raiseError(new Exception(s"Cannot parse task arguments: $str", ex))
        }

    JobTask(name, task.contramap(convert), onCancel.contramap(convert))
  }
} 
Example 149
Source File: TextExtract.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.extract.ocr

import cats.effect.{Blocker, ContextShift, Sync}
import fs2.Stream

import docspell.common._
import docspell.extract.internal.Text
import docspell.files._

object TextExtract {

  def extract[F[_]: Sync: ContextShift](
      in: Stream[F, Byte],
      blocker: Blocker,
      logger: Logger[F],
      lang: String,
      config: OcrConfig
  ): Stream[F, Text] =
    extractOCR(in, blocker, logger, lang, config)

  def extractOCR[F[_]: Sync: ContextShift](
      in: Stream[F, Byte],
      blocker: Blocker,
      logger: Logger[F],
      lang: String,
      config: OcrConfig
  ): Stream[F, Text] =
    Stream
      .eval(TikaMimetype.detect(in, MimeTypeHint.none))
      .flatMap({
        case MimeType.pdf =>
          Stream.eval(Ocr.extractPdf(in, blocker, logger, lang, config)).unNoneTerminate

        case mt if mt.primary == "image" =>
          Ocr.extractImage(in, blocker, logger, lang, config)

        case mt =>
          raiseError(s"File `$mt` not supported")
      })
      .map(Text.apply)

  private def raiseError[F[_]: Sync](msg: String): Stream[F, Nothing] =
    Stream.raiseError[F](new Exception(msg))
} 
Example 150
Source File: PdfboxExtract.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.extract.pdfbox

import java.io.InputStream
import java.nio.file.Path

import scala.util.{Try, Using}

import cats.effect.Sync
import cats.implicits._
import fs2.Stream

import docspell.extract.internal.Text

import org.apache.pdfbox.pdmodel.PDDocument
import org.apache.pdfbox.text.PDFTextStripper

object PdfboxExtract {

  def get[F[_]: Sync](data: Stream[F, Byte]): F[Either[Throwable, Text]] =
    data.compile
      .to(Array)
      .map(bytes => Using(PDDocument.load(bytes))(readText).toEither.flatten)

  def get(is: InputStream): Either[Throwable, Text] =
    Using(PDDocument.load(is))(readText).toEither.flatten

  def get(inFile: Path): Either[Throwable, Text] =
    Using(PDDocument.load(inFile.toFile))(readText).toEither.flatten

  private def readText(doc: PDDocument): Either[Throwable, Text] =
    Try {
      val stripper = new PDFTextStripper()
      stripper.setAddMoreFormatting(true)
      stripper.setLineSeparator("\n")
      Text(Option(stripper.getText(doc)))
    }.toEither
} 
Example 151
Source File: RtfExtract.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.extract.rtf

import java.io.{ByteArrayInputStream, InputStream}
import javax.swing.text.rtf.RTFEditorKit

import scala.util.Try

import cats.effect.Sync
import cats.implicits._
import fs2.Stream

import docspell.common.MimeType
import docspell.extract.internal.Text

object RtfExtract {

  val rtfType = MimeType.application("rtf")

  def get(is: InputStream): Either[Throwable, Text] =
    Try {
      val kit = new RTFEditorKit()
      val doc = kit.createDefaultDocument()
      kit.read(is, doc, 0)
      Text(doc.getText(0, doc.getLength))
    }.toEither

  def get[F[_]: Sync](data: Stream[F, Byte]): F[Either[Throwable, Text]] =
    data.compile.to(Array).map(new ByteArrayInputStream(_)).map(get)
} 
Example 152
Source File: PoiExtract.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.extract.poi

import java.io.{ByteArrayInputStream, InputStream}

import scala.util.Try

import cats.data.EitherT
import cats.effect.Sync
import cats.implicits._
import fs2.Stream

import docspell.common._
import docspell.extract.internal.Text
import docspell.files.TikaMimetype

import org.apache.poi.hssf.extractor.ExcelExtractor
import org.apache.poi.hssf.usermodel.HSSFWorkbook
import org.apache.poi.hwpf.extractor.WordExtractor
import org.apache.poi.xssf.extractor.XSSFExcelExtractor
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.apache.poi.xwpf.extractor.XWPFWordExtractor
import org.apache.poi.xwpf.usermodel.XWPFDocument

object PoiExtract {

  def get[F[_]: Sync](
      data: Stream[F, Byte],
      hint: MimeTypeHint
  ): F[Either[Throwable, Text]] =
    TikaMimetype.detect(data, hint).flatMap(mt => get(data, mt))

  def get[F[_]: Sync](
      data: Stream[F, Byte],
      mime: MimeType
  ): F[Either[Throwable, Text]] =
    mime match {
      case PoiType.doc =>
        getDoc(data)
      case PoiType.xls =>
        getXls(data)
      case PoiType.xlsx =>
        getXlsx(data)
      case PoiType.docx =>
        getDocx(data)
      case PoiType.msoffice =>
        EitherT(getDoc[F](data))
          .recoverWith({
            case _ => EitherT(getXls[F](data))
          })
          .value
      case PoiType.ooxml =>
        EitherT(getDocx[F](data))
          .recoverWith({
            case _ => EitherT(getXlsx[F](data))
          })
          .value
      case mt =>
        Sync[F].pure(Left(new Exception(s"Unsupported content: ${mt.asString}")))
    }

  def getDocx(is: InputStream): Either[Throwable, Text] =
    Try {
      val xt = new XWPFWordExtractor(new XWPFDocument(is))
      Text(Option(xt.getText))
    }.toEither

  def getDoc(is: InputStream): Either[Throwable, Text] =
    Try {
      val xt = new WordExtractor(is)
      Text(Option(xt.getText))
    }.toEither

  def getXlsx(is: InputStream): Either[Throwable, Text] =
    Try {
      val xt = new XSSFExcelExtractor(new XSSFWorkbook(is))
      Text(Option(xt.getText))
    }.toEither

  def getXls(is: InputStream): Either[Throwable, Text] =
    Try {
      val xt = new ExcelExtractor(new HSSFWorkbook(is))
      Text(Option(xt.getText))
    }.toEither

  def getDocx[F[_]: Sync](data: Stream[F, Byte]): F[Either[Throwable, Text]] =
    data.compile.to(Array).map(new ByteArrayInputStream(_)).map(getDocx)

  def getDoc[F[_]: Sync](data: Stream[F, Byte]): F[Either[Throwable, Text]] =
    data.compile.to(Array).map(new ByteArrayInputStream(_)).map(getDoc)

  def getXlsx[F[_]: Sync](data: Stream[F, Byte]): F[Either[Throwable, Text]] =
    data.compile.to(Array).map(new ByteArrayInputStream(_)).map(getXlsx)

  def getXls[F[_]: Sync](data: Stream[F, Byte]): F[Either[Throwable, Text]] =
    data.compile.to(Array).map(new ByteArrayInputStream(_)).map(getXls)

} 
Example 153
Source File: StreamSyntax.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.common.syntax

import cats.effect.Sync
import cats.implicits._
import fs2.Stream

import io.circe._
import io.circe.parser._

trait StreamSyntax {

  implicit class StringStreamOps[F[_]](s: Stream[F, String]) {

    def parseJsonAs[A](implicit d: Decoder[A], F: Sync[F]): F[Either[Throwable, A]] =
      s.fold("")(_ + _)
        .compile
        .last
        .map(optStr =>
          for {
            str <-
              optStr
                .map(_.trim)
                .toRight(new Exception("Empty string cannot be parsed into a value"))
            json  <- parse(str).leftMap(_.underlying)
            value <- json.as[A]
          } yield value
        )

  }

} 
Example 154
Source File: Ident.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.common

import java.security.SecureRandom
import java.util.UUID

import cats.Eq
import cats.effect.Sync
import cats.implicits._

import io.circe.{Decoder, Encoder}
import scodec.bits.ByteVector

case class Ident(id: String) {
  def isEmpty: Boolean =
    id.trim.isEmpty

  def nonEmpty: Boolean =
    !isEmpty

  def /(next: Ident): Ident =
    new Ident(id + "." + next.id)
}

object Ident {
  implicit val identEq: Eq[Ident] =
    Eq.by(_.id)

  val chars: Set[Char] = (('A' to 'Z') ++ ('a' to 'z') ++ ('0' to '9') ++ "-_.").toSet

  def randomUUID[F[_]: Sync]: F[Ident] =
    Sync[F].delay(unsafe(UUID.randomUUID.toString))

  def randomId[F[_]: Sync]: F[Ident] =
    Sync[F].delay {
      val random = new SecureRandom()
      val buffer = new Array[Byte](32)
      random.nextBytes(buffer)
      unsafe(ByteVector.view(buffer).toBase58.grouped(11).mkString("-"))
    }

  def apply(str: String): Either[String, Ident] =
    fromString(str)

  def fromString(s: String): Either[String, Ident] =
    if (s.forall(chars.contains)) Right(new Ident(s))
    else Left(s"Invalid identifier: '$s'. Allowed chars: ${chars.toList.sorted.mkString}")

  def fromBytes(bytes: ByteVector): Ident =
    unsafe(bytes.toBase58)

  def fromByteArray(bytes: Array[Byte]): Ident =
    fromBytes(ByteVector.view(bytes))

  def unsafe(s: String): Ident =
    fromString(s) match {
      case Right(id) => id
      case Left(err) => sys.error(err)
    }

  def unapply(arg: String): Option[Ident] =
    fromString(arg).toOption

  implicit val encodeIdent: Encoder[Ident] =
    Encoder.encodeString.contramap(_.id)

  implicit val decodeIdent: Decoder[Ident] =
    Decoder.decodeString.emap(Ident.fromString)

} 
Example 155
Source File: InfoRoutes.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.restserver.routes

import cats.effect.Sync

import docspell.restapi.model.VersionInfo
import docspell.restserver.BuildInfo

import org.http4s.HttpRoutes
import org.http4s.circe.CirceEntityEncoder._
import org.http4s.dsl.Http4sDsl

object InfoRoutes {

  def apply[F[_]: Sync](): HttpRoutes[F] = {
    val dsl = new Http4sDsl[F] {}
    import dsl._
    HttpRoutes.of[F] {
      case GET -> (Root / "version") =>
        Ok(
          VersionInfo(
            BuildInfo.version,
            BuildInfo.builtAtMillis,
            BuildInfo.builtAtString,
            BuildInfo.gitHeadCommit.getOrElse(""),
            BuildInfo.gitDescribedVersion.getOrElse("")
          )
        )
    }
  }
} 
Example 156
Source File: RNode.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.store.records

import cats.effect.Sync
import cats.implicits._

import docspell.common._
import docspell.store.impl.Column
import docspell.store.impl.Implicits._

import doobie._
import doobie.implicits._

case class RNode(
    id: Ident,
    nodeType: NodeType,
    url: LenientUri,
    updated: Timestamp,
    created: Timestamp
) {}

object RNode {

  def apply[F[_]: Sync](id: Ident, nodeType: NodeType, uri: LenientUri): F[RNode] =
    Timestamp.current[F].map(now => RNode(id, nodeType, uri, now, now))

  val table = fr"node"

  object Columns {
    val id       = Column("id")
    val nodeType = Column("type")
    val url      = Column("url")
    val updated  = Column("updated")
    val created  = Column("created")
    val all      = List(id, nodeType, url, updated, created)
  }
  import Columns._

  def insert(v: RNode): ConnectionIO[Int] =
    insertRow(
      table,
      all,
      fr"${v.id},${v.nodeType},${v.url},${v.updated},${v.created}"
    ).update.run

  def update(v: RNode): ConnectionIO[Int] =
    updateRow(
      table,
      id.is(v.id),
      commas(
        nodeType.setTo(v.nodeType),
        url.setTo(v.url),
        updated.setTo(v.updated)
      )
    ).update.run

  def set(v: RNode): ConnectionIO[Int] =
    for {
      n <- update(v)
      k <- if (n == 0) insert(v) else 0.pure[ConnectionIO]
    } yield n + k

  def delete(appId: Ident): ConnectionIO[Int] =
    (fr"DELETE FROM" ++ table ++ where(id.is(appId))).update.run

  def findAll(nt: NodeType): ConnectionIO[Vector[RNode]] =
    selectSimple(all, table, nodeType.is(nt)).query[RNode].to[Vector]

  def findById(nodeId: Ident): ConnectionIO[Option[RNode]] =
    selectSimple(all, table, id.is(nodeId)).query[RNode].option
} 
Example 157
Source File: RInvitation.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.store.records

import cats.effect.Sync
import cats.implicits._

import docspell.common._
import docspell.store.impl.Implicits._
import docspell.store.impl._

import doobie._
import doobie.implicits._

case class RInvitation(id: Ident, created: Timestamp) {}

object RInvitation {

  val table = fr"invitation"

  object Columns {
    val id      = Column("id")
    val created = Column("created")
    val all     = List(id, created)
  }
  import Columns._

  def generate[F[_]: Sync]: F[RInvitation] =
    for {
      c <- Timestamp.current[F]
      i <- Ident.randomId[F]
    } yield RInvitation(i, c)

  def insert(v: RInvitation): ConnectionIO[Int] =
    insertRow(table, all, fr"${v.id},${v.created}").update.run

  def insertNew: ConnectionIO[RInvitation] =
    generate[ConnectionIO].flatMap(v => insert(v).map(_ => v))

  def findById(invite: Ident): ConnectionIO[Option[RInvitation]] =
    selectSimple(all, table, id.is(invite)).query[RInvitation].option

  def delete(invite: Ident): ConnectionIO[Int] =
    deleteFrom(table, id.is(invite)).update.run

  def useInvite(invite: Ident, minCreated: Timestamp): ConnectionIO[Boolean] = {
    val get = selectCount(id, table, and(id.is(invite), created.isGt(minCreated)))
      .query[Int]
      .unique
    for {
      inv <- get
      _   <- delete(invite)
    } yield inv > 0
  }

  def deleteOlderThan(ts: Timestamp): ConnectionIO[Int] =
    deleteFrom(table, created.isLt(ts)).update.run
} 
Example 158
Source File: FlywayMigrate.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.store.migrate

import cats.effect.Sync

import docspell.store.JdbcConfig

import org.flywaydb.core.Flyway
import org.log4s._

object FlywayMigrate {
  private[this] val logger = getLogger

  def run[F[_]: Sync](jdbc: JdbcConfig): F[Int] =
    Sync[F].delay {
      logger.info("Running db migrations...")
      val locations = jdbc.dbmsName match {
        case Some(dbtype) =>
          val name = if (dbtype == "h2") "postgresql" else dbtype
          List(s"classpath:db/migration/${name}")
        case None =>
          logger.warn(
            s"Cannot read database name from jdbc url: ${jdbc.url}. Go with PostgreSQL"
          )
          List("classpath:db/postgresql")
      }

      logger.info(s"Using migration locations: $locations")
      val fw = Flyway
        .configure()
        .cleanDisabled(true)
        .dataSource(jdbc.url.asString, jdbc.user, jdbc.password)
        .locations(locations: _*)
        .load()

      fw.repair()
      fw.migrate()
    }
} 
Example 159
Source File: TestSupport.scala    From cedi-dtrace   with Apache License 2.0 5 votes vote down vote up
package com.ccadllc.cedi.dtrace
package logging

import cats.effect.{ IO, Sync }

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

import org.scalacheck.Arbitrary

import org.scalatest.Suite
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike

import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks

import shapeless.Lazy

trait TestSupport extends AnyWordSpecLike with Matchers with ScalaCheckPropertyChecks with TraceGenerators with TestData {
  self: Suite =>

  override def testEmitter[F[_]: Sync]: F[TraceSystem.Emitter[F]] = Sync[F].pure(LogEmitter.apply)

  val salesManagementSystem = TraceSystem(testSystemData, testEmitter[IO].unsafeRunSync, TraceSystem.realTimeTimer[IO])
  val calculateQuarterlySalesTraceContext = TraceContext(quarterlySalesCalculationSpan, true, salesManagementSystem)

  def encodeGeneratedJson[A: Arbitrary](implicit encoder: Lazy[Encoder[A]]): Unit = {
    implicit val e = encoder.value
    "encode arbitrary instances to JSON" in {
      forAll { (msg: A) => msg.asJson.noSpaces should not be (None) }
    }
  }
  def encodeSpecificJson[A](a: A, json: Json)(implicit encoder: Lazy[Encoder[A]]): Unit = {
    implicit val e = encoder.value
    "encode specific instance to JSON and ensure it matches expected" in { a.asJson shouldBe json }
  }
} 
Example 160
Source File: LogstashLogbackEmitter.scala    From cedi-dtrace   with Apache License 2.0 5 votes vote down vote up
package com.ccadllc.cedi.dtrace
package logstash

import cats.effect.Sync
import cats.implicits._

import net.logstash.logback.marker.LogstashMarker
import net.logstash.logback.marker.Markers._

import org.slf4j.LoggerFactory

import scala.collection.JavaConverters._


@deprecated("use EcsLogstashLogbackEmitter", "2.0.0")
final class LogstashLogbackEmitter[F[_]](implicit F: Sync[F]) extends TraceSystem.Emitter[F] {
  private val logger = LoggerFactory.getLogger("distributed-trace.logstash")

  final val description: String = "Logstash Logback Emitter"
  final def emit(tc: TraceContext[F]): F[Unit] = F.delay {
    if (logger.isDebugEnabled) {
      val s = tc.currentSpan
      val marker: LogstashMarker =
        append("where", tc.system.data.allValues.asJava).
          and[LogstashMarker](append("root", s.root)).
          and[LogstashMarker](append("trace-id", s.spanId.traceId.toString)).
          and[LogstashMarker](append("span-id", s.spanId.spanId)).
          and[LogstashMarker](append("parent-id", s.spanId.parentSpanId)).
          and[LogstashMarker](append("span-name", s.spanName.value)).
          and[LogstashMarker](append("start-time", s.startTime.show)).
          and[LogstashMarker](append("span-success", s.failure.isEmpty)).
          and[LogstashMarker](append("failure-detail", s.failure.map(_.render).orNull)).
          and[LogstashMarker](append("span-duration", s.duration.toMicros)).
          and[LogstashMarker](append("notes", s.notes.map(n => n.name.value -> n.value).collect { case (name, Some(value)) => name -> value.toString }.toMap.asJava))
      logger.debug(marker, "Span {} {} after {} microseconds",
        s.spanName.value,
        if (s.failure.isEmpty) "succeeded" else "failed",
        s.duration.toMicros.toString)
    }
  }
} 
Example 161
Source File: EcsLogstashLogbackEmitter.scala    From cedi-dtrace   with Apache License 2.0 5 votes vote down vote up
package com.ccadllc.cedi.dtrace
package logstash

import cats.effect.Sync
import cats.implicits._

import net.logstash.logback.marker.LogstashMarker
import net.logstash.logback.marker.Markers._

import org.slf4j.LoggerFactory

import scala.collection.JavaConverters._


final class EcsLogstashLogbackEmitter[F[_]](implicit F: Sync[F]) extends TraceSystem.Emitter[F] {
  object ecs {
    object field {
      val kind: String = "event.kind"
      val module: String = "event.module"
      val root: String = "dtrace.root"
      val traceId: String = "dtrace.trace_id"
      val parentId: String = "dtrace.parent_id"
      val spanId: String = "event.id"
      val spanName: String = "event.action"
      val spanStart: String = "event.start"
      val spanOutcome: String = "event.outcome"
      val spanDuration: String = "event.duration"
      val spanFailureDetail: String = "error.message"
      val spanMetadata: String = "labels"
    }
  }
  private val logger = LoggerFactory.getLogger("distributed-trace.ecs.logstash")
  final val description: String = "ECS-Compliant Logstash Logback Emitter"
  final def emit(tc: TraceContext[F]): F[Unit] = F.delay {
    if (logger.isDebugEnabled) {
      val s = tc.currentSpan
      val marker: LogstashMarker = {
        val m = append(ecs.field.kind, "event").
          and[LogstashMarker](append(ecs.field.module, "dtrace")).
          and[LogstashMarker](append(ecs.field.root, s.root)).
          and[LogstashMarker](append(ecs.field.traceId, s.spanId.traceId.toString)).
          and[LogstashMarker](append(ecs.field.parentId, s.spanId.parentSpanId)).
          and[LogstashMarker](append(ecs.field.spanId, s.spanId.spanId)).
          and[LogstashMarker](append(ecs.field.spanName, s.spanName.value)).
          and[LogstashMarker](append(ecs.field.spanStart, s.startTime.show)).
          and[LogstashMarker](append(ecs.field.spanOutcome, if (s.failure.isEmpty) "success" else "failure")).
          and[LogstashMarker](append(ecs.field.spanDuration, s.duration.toUnit(tc.system.timer.unit))).
          and[LogstashMarker](append(ecs.field.spanFailureDetail, s.failure.map(_.render).orNull)).
          and[LogstashMarker](append(
            ecs.field.spanMetadata,
            (tc.system.data.meta.values ++ s.notes.map(
              n => n.name.value -> n.value).collect { case (name, Some(value)) => name -> value.toString }.toMap).asJava))
        tc.system.data.identity.values.foldLeft(m) { case (acc, (k, v)) => acc.and[LogstashMarker](append(k, v)) }
      }
      logger.debug(marker, s"Span {} {} after {} ${tc.system.timer.unit.toString.toLowerCase}s",
        s.spanName.value,
        if (s.failure.isEmpty) "succeeded" else "failed",
        s.duration.toUnit(tc.system.timer.unit).toString)
    }
  }
} 
Example 162
Source File: TestEmitter.scala    From cedi-dtrace   with Apache License 2.0 5 votes vote down vote up
package com.ccadllc.cedi.dtrace

import cats.effect.Sync

class TestEmitter[F[_]](implicit F: Sync[F]) extends TraceSystem.Emitter[F] {
  class EmitterTestCache {
    case class EmitterTestEntry(msg: String, tc: TraceContext[F])
    private var emitterLogCache: Vector[EmitterTestEntry] = Vector.empty
    def put(msg: String, tc: TraceContext[F]): Unit = synchronized { emitterLogCache = emitterLogCache :+ EmitterTestEntry(msg.toLowerCase, tc) }
    def all: Vector[EmitterTestEntry] = emitterLogCache
    def containingAll(substrings: String*): Vector[EmitterTestEntry] = {
      def containing(substrings: Seq[String])(predicate: (EmitterTestEntry, Seq[String]) => Boolean): Vector[EmitterTestEntry] = {
        require(!substrings.isEmpty)
        val lowerSubstrings = substrings map { _.toLowerCase }
        emitterLogCache filter { predicate(_, lowerSubstrings) }
      }
      containing(substrings) { (e, strings) => strings forall { e.msg.contains } }
    }
  }
  val cache = new EmitterTestCache
  override def description: String = "Test Emitter"
  override def emit(tc: TraceContext[F]): F[Unit] = {
    def formatText(context: TraceContext[F]) = {
      s"Span: [ span-id=${context.currentSpan.spanId.spanId} ] [ trace-id=${context.currentSpan.spanId.traceId} ] [ parent-id=${context.currentSpan.spanId.parentSpanId} ] [ span-name=${context.currentSpan.spanName} ] [ system-data=${context.system.data.description} ] [ start-time=${context.currentSpan.startTime} ] [ span-duration=${context.currentSpan.duration} ] [ span-success=${context.currentSpan.failure.isEmpty} ] [ failure-detail=${context.currentSpan.failure.fold("N/A")(_.render)} ][ notes=[${context.currentSpan.notes.mkString("] [")}] ]"
    }
    F.delay(cache.put(formatText(tc), tc))
  }
} 
Example 163
Source File: TestClient.scala    From franklin   with Apache License 2.0 5 votes vote down vote up
package com.azavea.franklin.api

import cats.effect.Resource
import cats.effect.Sync
import cats.implicits._
import com.azavea.franklin.api.services.{CollectionItemsService, CollectionsService}
import com.azavea.stac4s.{StacCollection, StacItem}
import eu.timepit.refined.auto._
import io.circe.syntax._
import org.http4s.circe.CirceEntityDecoder._
import org.http4s.circe.CirceEntityEncoder._
import org.http4s.implicits._
import org.http4s.{Method, Request, Uri}

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


class TestClient[F[_]: Sync](
    collectionsService: CollectionsService[F],
    collectionItemsService: CollectionItemsService[F]
) {

  private def createCollection(collection: StacCollection): F[StacCollection] =
    collectionsService.routes.orNotFound.run(
      Request(
        method = Method.POST,
        uri = Uri.unsafeFromString("/collections")
      ).withEntity(collection.asJson)
    ) flatMap { _.as[StacCollection] }

  private def deleteCollection(collection: StacCollection): F[Unit] = {
    val encodedCollectionId = URLEncoder.encode(collection.id, StandardCharsets.UTF_8.toString)
    collectionsService.routes.orNotFound
      .run(
        Request(
          method = Method.DELETE,
          uri = Uri.unsafeFromString(s"/collections/$encodedCollectionId")
        )
      )
      .void
  }

  private def createItemInCollection(collection: StacCollection, item: StacItem): F[StacItem] = {
    val encodedCollectionId = URLEncoder.encode(collection.id, StandardCharsets.UTF_8.toString)
    collectionItemsService.routes.orNotFound.run(
      Request(
        method = Method.POST,
        uri = Uri.unsafeFromString(s"/collections/$encodedCollectionId/items")
      ).withEntity(item)
    ) flatMap { _.as[StacItem] }
  }

  private def deleteItemInCollection(collection: StacCollection, item: StacItem): F[Unit] = {
    val encodedCollectionId = URLEncoder.encode(collection.id, StandardCharsets.UTF_8.toString)
    val encodedItemId       = URLEncoder.encode(item.id, StandardCharsets.UTF_8.toString)
    collectionItemsService.routes.orNotFound
      .run(
        Request(
          method = Method.DELETE,
          uri = Uri.unsafeFromString(s"/collections/$encodedCollectionId/items/$encodedItemId")
        )
      )
      .void
  }

  def getItemResource(collection: StacCollection, item: StacItem): Resource[F, StacItem] =
    Resource.make(createItemInCollection(collection, item.copy(collection = Some(collection.id))))(
      item => deleteItemInCollection(collection, item)
    )

  def getCollectionResource(collection: StacCollection): Resource[F, StacCollection] =
    Resource.make(createCollection(collection))(collection => deleteCollection(collection))

  def getCollectionItemResource(
      item: StacItem,
      collection: StacCollection
  ): Resource[F, (StacItem, StacCollection)] =
    (getItemResource(collection, item), getCollectionResource(collection)).tupled
} 
Example 164
Source File: TestServices.scala    From franklin   with Apache License 2.0 5 votes vote down vote up
package com.azavea.franklin.api

import cats.effect.{ContextShift, Sync}
import com.azavea.franklin.api.commands.ApiConfig
import com.azavea.franklin.api.services.{CollectionItemsService, CollectionsService, SearchService}
import doobie.Transactor
import eu.timepit.refined.types.numeric.{NonNegInt, PosInt}

class TestServices[F[_]: Sync](xa: Transactor[F])(implicit cs: ContextShift[F]) {

  val apiConfig: ApiConfig =
    ApiConfig(PosInt(9090), PosInt(9090), "localhost", "http", NonNegInt(30), true, false)

  val searchService: SearchService[F] =
    new SearchService[F](apiConfig.apiHost, NonNegInt(30), apiConfig.enableTiles, xa)

  val collectionsService: CollectionsService[F] = new CollectionsService[F](
    xa,
    apiConfig
  )

  val collectionItemsService: CollectionItemsService[F] = new CollectionItemsService[F](
    xa,
    apiConfig
  )

} 
Example 165
Source File: TestConsole.scala    From console4cats   with Apache License 2.0 5 votes vote down vote up
package cats.effect.test

import cats._
import cats.data.Chain
import cats.effect.concurrent.Ref
import cats.effect.{ Console, Sync }
import cats.syntax.functor._
import cats.syntax.show._

private class TestConsole[F[_]: Applicative](
    outLines: Ref[F, Chain[String]],
    outWords: Ref[F, Chain[String]],
    outErrors: Ref[F, Chain[String]],
    val readLn: F[String]
) extends Console[F] {

  override def putStrLn[A: Show](a: A): F[Unit] =
    outLines.update(_.append(a.show))

  override def putStr[A: Show](a: A): F[Unit] =
    outWords.update(_.append(a.show))

  override def putError[A: Show](a: A): F[Unit] =
    outErrors.update(_.append(a.show))
}

object TestConsole {

  
    def sequenceAndDefault[F[_]: Sync](
        inputs: Chain[String],
        default: String
    ): F[F[String]] =
      Ref[F].of(inputs).map {
        _.modify {
          _.uncons match {
            case Some((head, tail)) => (tail, head)
            case None               => (Chain.nil, default)
          }
        }
      }
  }
} 
Example 166
Source File: TracedPrograms.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer.module.tracer

import cats.Parallel
import cats.effect.Sync
import cats.syntax.apply._
import dev.profunktor.tracer.Trace.Trace
import dev.profunktor.tracer.TracerLog
import dev.profunktor.tracer.algebra.UserAlgebra
import dev.profunktor.tracer.model.user.{User, Username}
import dev.profunktor.tracer.module.{LivePrograms, Programs}

case class TracedPrograms[F[_]: Parallel: Sync: λ[T[_] => TracerLog[Trace[T, ?]]]](
    repos: TracedRepositories[F],
    clients: TracedHttpClients[F]
) extends Programs[Trace[F, ?]] {
  private val programs = LivePrograms[Trace[F, ?]](repos, clients)

  override val users: UserAlgebra[Trace[F, ?]] = new UserTracer[F](programs.users)
}

private[tracer] final class UserTracer[F[_]: Sync](
    users: UserAlgebra[Trace[F, ?]]
)(implicit L: TracerLog[Trace[F, ?]])
    extends UserAlgebra[Trace[F, ?]] {

  override def find(username: Username): Trace[F, User] =
    L.info[UserAlgebra[F]](s"Find user by username: ${username.value}") *> users.find(username)

  override def persist(user: User): Trace[F, Unit] =
    L.info[UserAlgebra[F]](s"About to persist user: ${user.username.value}") *> users.persist(user)

} 
Example 167
Source File: TracedHttpClients.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer.module.tracer

import cats.effect.Sync
import cats.syntax.apply._
import dev.profunktor.tracer.Trace.Trace
import dev.profunktor.tracer.http.client.UserRegistry
import dev.profunktor.tracer.model.user.User
import dev.profunktor.tracer.module.{HttpClients, LiveHttpClients}
import dev.profunktor.tracer.{Trace, TracerLog}
import org.http4s.client.Client

case class TracedHttpClients[F[_]: Sync: λ[T[_] => TracerLog[Trace[T, ?]]]] private (
    client: Client[F]
) extends HttpClients[Trace[F, ?]] {
  private val clients = LiveHttpClients[F](client)

  override val userRegistry: UserRegistry[Trace[F, ?]] = new TracedUserRegistry[F](clients.userRegistry)
}

private[tracer] final class TracedUserRegistry[F[_]: Sync](
    registry: UserRegistry[F]
)(implicit L: TracerLog[Trace[F, ?]])
    extends UserRegistry[Trace[F, ?]] {

  override def register(user: User): Trace[F, Unit] =
    L.info[UserRegistry[F]](s"Registering user: ${user.username.value}") *>
      Trace(_ => registry.register(user))

} 
Example 168
Source File: Repositories.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer.module

import cats.effect.Sync
import cats.syntax.functor._
import dev.profunktor.tracer.repository.algebra.UserRepository
import dev.profunktor.tracer.repository.interpreter.MemUserRepository

private[module] trait Repositories[F[_]] {
  def users: UserRepository[F]
}

object LiveRepositories {
  def apply[F[_]: Sync]: F[Repositories[F]] =
    MemUserRepository.create[F].map(new LiveRepositories[F](_))
}

final class LiveRepositories[F[_]](usersRepo: UserRepository[F]) extends Repositories[F] {
  val users: UserRepository[F] = usersRepo
} 
Example 169
Source File: MemUserRepository.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer.repository.interpreter

import cats.effect.Sync
import cats.effect.concurrent.Ref
import cats.syntax.all._
import dev.profunktor.tracer.model.user.{User, Username}
import dev.profunktor.tracer.repository.algebra.UserRepository

object MemUserRepository {
  def create[F[_]: Sync]: F[UserRepository[F]] =
    Ref.of[F, Map[Username, User]](Map.empty).map(new MemUserRepository[F](_))
}

class MemUserRepository[F[_]: Sync] private (
    state: Ref[F, Map[Username, User]]
) extends UserRepository[F] {

  def find(username: Username): F[Option[User]] =
    state.get.map(_.get(username))

  def persist(user: User): F[Unit] =
    state.update(_.updated(user.username, user))

} 
Example 170
Source File: UserRoutes.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer.http

import cats.effect.Sync
import cats.syntax.all._
import dev.profunktor.tracer.Trace._
import dev.profunktor.tracer.algebra.UserAlgebra
import dev.profunktor.tracer.model.user.{User, Username}
import dev.profunktor.tracer.model.errors.UserError._
import dev.profunktor.tracer.{Http4sTracerDsl, TracedHttpRoute, Tracer}
import org.http4s._
import org.http4s.server.Router

class UserRoutes[F[_]: Sync: Tracer](users: UserAlgebra[Trace[F, ?]]) extends Http4sTracerDsl[F] {

  private[http] val PathPrefix = "/users"

  private val httpRoutes: HttpRoutes[F] = TracedHttpRoute[F] {
    case GET -> Root / username using traceId =>
      users
        .find(Username(username))
        .run(traceId)
        .flatMap(user => Ok(user))
        .handleErrorWith {
          case UserNotFound(u) => NotFound(u.value)
        }

    case tr @ POST -> Root using traceId =>
      tr.request.decode[User] { user =>
        users
          .persist(user)
          .run(traceId)
          .flatMap(_ => Created())
          .handleErrorWith {
            case UserAlreadyExists(u) => Conflict(u.value)
          }
      }
  }

  val routes: HttpRoutes[F] = Router(
    PathPrefix -> httpRoutes
  )

} 
Example 171
Source File: UserRegistry.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer.http
package client

import cats.effect.Sync
import cats.syntax.functor._
import dev.profunktor.tracer.model.user.User
import io.circe.syntax._
import org.http4s.Method._
import org.http4s.Uri
import org.http4s.client.Client
import org.http4s.client.dsl.Http4sClientDsl

trait UserRegistry[F[_]] {
  def register(user: User): F[Unit]
}

final case class LiveUserRegistry[F[_]: Sync](
    client: Client[F]
) extends UserRegistry[F]
    with Http4sClientDsl[F] {

  private val uri = Uri.uri("https://jsonplaceholder.typicode.com/posts")

  def register(user: User): F[Unit] =
    client.successful(POST(user.asJson, uri)).void
} 
Example 172
Source File: AuthRoutes.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer.http

import cats.effect.Sync
import cats.syntax.flatMap._
import dev.profunktor.tracer.Trace._
import dev.profunktor.tracer.Tracer
import dev.profunktor.tracer.algebra.UserAlgebra
import dev.profunktor.tracer.auth.{AuthTracedHttpRoute, Http4sAuthTracerDsl}
import dev.profunktor.tracer.model.user.Username
import io.circe.generic.auto._
import org.http4s._
import org.http4s.server.{AuthMiddleware, Router}

class AuthRoutes[F[_]: Sync: Tracer](users: UserAlgebra[Trace[F, ?]]) extends Http4sAuthTracerDsl[F] {

  private[http] val PathPrefix = "/auth"

  private val httpRoutes: AuthedRoutes[String, F] = AuthTracedHttpRoute[String, F] {
    case GET -> Root as user using traceId =>
      users.find(Username(user)).run(traceId) >> Ok(user -> traceId)

    case POST -> Root as user using traceId =>
      Created(user -> traceId)
  }

  lazy val authMiddleware: AuthMiddleware[F, String] = null

  lazy val routes: HttpRoutes[F] = Router(
    PathPrefix -> authMiddleware(httpRoutes)
  )

} 
Example 173
Source File: Tracer.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer

import cats.Applicative
import cats.data.Kleisli
import cats.effect.Sync
import cats.syntax.all._
import org.http4s.syntax.StringSyntax
import org.http4s.{Header, HttpApp, Request}


object Tracer extends StringSyntax {

  private[tracer] val DefaultTraceIdHeader = "Trace-Id"

  final case class TraceId(value: String) extends AnyVal {
    override def toString = s"[Trace-Id] - [$value]"
  }

  def apply[F[_]](implicit ev: Tracer[F]): Tracer[F] = ev

  def create[F[_]](headerName: String = DefaultTraceIdHeader): Tracer[F] = new Tracer[F](headerName)

}

class Tracer[F[_]] private (headerName: String) {

  import Trace._, Tracer._

  def middleware(
      http: HttpApp[F],
      logRequest: Boolean = false,
      logResponse: Boolean = false
  )(implicit F: Sync[F], L: TracerLog[Trace[F, ?]]): HttpApp[F] =
    Kleisli { req =>
      val createId: F[(Request[F], TraceId)] =
        for {
          id <- GenUUID.make[F]
          tr <- F.delay(req.putHeaders(Header(headerName, id.value)))
        } yield (tr, id)

      for {
        mi       <- getTraceId(req)
        (tr, id) <- mi.fold(createId)(id => (req, id).pure[F])
        _        <- if (logRequest) L.info[Tracer[F]](s"$req").run(id) else F.unit
        rs       <- http(tr).map(_.putHeaders(Header(headerName, id.value)))
        _        <- if (logResponse) L.info[Tracer[F]](s"$rs").run(id) else F.unit
      } yield rs
    }

  def loggingMiddleware(
      http: HttpApp[F]
  )(implicit F: Sync[F], L: TracerLog[Trace[F, ?]]): HttpApp[F] =
    middleware(http, logRequest = true, logResponse = true)

  def getTraceId(request: Request[F])(implicit F: Applicative[F]): F[Option[TraceId]] =
    F.pure(request.headers.get(headerName.ci).map(h => TraceId(h.value)))

} 
Example 174
Source File: tracerlog.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer.instances

import cats.effect.Sync
import cats.syntax.flatMap._
import dev.profunktor.tracer.Trace
import dev.profunktor.tracer.Trace._
import dev.profunktor.tracer.TracerLog
import org.slf4j.{Logger, LoggerFactory}

import scala.reflect.ClassTag

object tracerlog {

  implicit def defaultLog[F[_]](implicit F: Sync[F]): TracerLog[Trace[F, ?]] =
    new TracerLog[Trace[F, ?]] {
      def logger[A](implicit ct: ClassTag[A]): F[Logger] =
        F.delay(LoggerFactory.getLogger(ct.runtimeClass))

      override def info[A: ClassTag](value: => String): Trace[F, Unit] = Trace { id =>
        logger[A].flatMap { log =>
          if (log.isInfoEnabled) F.delay(log.info(s"$id - $value"))
          else F.unit
        }
      }

      override def error[A: ClassTag](value: => String): Trace[F, Unit] = Trace { id =>
        logger[A].flatMap { log =>
          if (log.isErrorEnabled) F.delay(log.error(s"$id - $value"))
          else F.unit
        }
      }

      override def warn[A: ClassTag](value: => String): Trace[F, Unit] = Trace { id =>
        logger[A].flatMap { log =>
          if (log.isWarnEnabled) F.delay(log.warn(s"$id - $value"))
          else F.unit
        }
      }
    }

} 
Example 175
Source File: Fixture.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.sourcing.projections

import akka.persistence.journal.{Tagged, WriteEventAdapter}
import cats.effect.{IO, Sync}
import cats.effect.concurrent.Ref

import scala.annotation.nowarn

object Fixture {

  sealed trait Event
  final case object Executed           extends Event
  final case object OtherExecuted      extends Event
  final case object AnotherExecuted    extends Event
  final case object YetAnotherExecuted extends Event
  final case object RetryExecuted      extends Event
  final case object IgnoreExecuted     extends Event
  final case object NotDiscarded       extends Event
  final case object Discarded          extends Event

  sealed trait EventTransform
  final case object ExecutedTransform           extends EventTransform
  final case object OtherExecutedTransform      extends EventTransform
  final case object AnotherExecutedTransform    extends EventTransform
  final case object YetAnotherExecutedTransform extends EventTransform
  final case object RetryExecutedTransform      extends EventTransform
  final case object IgnoreExecutedTransform     extends EventTransform
  final case object NotDiscardedTransform       extends EventTransform
  final case object DiscardedTransform          extends EventTransform

  sealed trait Cmd
  final case object Execute           extends Cmd
  final case object ExecuteOther      extends Cmd
  final case object ExecuteAnother    extends Cmd
  final case object ExecuteYetAnother extends Cmd
  final case object ExecuteRetry      extends Cmd
  final case object ExecuteIgnore     extends Cmd

  sealed trait State
  final case object Perpetual extends State

  sealed trait Rejection
  final case object Reject extends Rejection

  class TaggingAdapter extends WriteEventAdapter {
    override def manifest(event: Any): String = ""
    override def toJournal(event: Any): Any   =
      event match {
        case Executed           => Tagged(event, Set("executed"))
        case OtherExecuted      => Tagged(event, Set("other"))
        case AnotherExecuted    => Tagged(event, Set("another"))
        case YetAnotherExecuted => Tagged(event, Set("yetanother"))
        case RetryExecuted      => Tagged(event, Set("retry"))
        case IgnoreExecuted     => Tagged(event, Set("ignore"))
        case NotDiscarded       => Tagged(event, Set("discard"))
        case Discarded          => Tagged(event, Set("discard"))

      }
  }

  val initial: State                                             = Perpetual
  @nowarn("cat=unused")
  def next(state: State, event: Event): State                    = Perpetual
  @nowarn("cat=unused")
  def eval(state: State, cmd: Cmd): IO[Either[Rejection, Event]] =
    cmd match {
      case Execute           => IO.pure(Right(Executed))
      case ExecuteOther      => IO.pure(Right(OtherExecuted))
      case ExecuteAnother    => IO.pure(Right(AnotherExecuted))
      case ExecuteYetAnother => IO.pure(Right(YetAnotherExecuted))
      case ExecuteRetry      => IO.pure(Right(RetryExecuted))
      case ExecuteIgnore     => IO.pure(Right(IgnoreExecuted))

    }

  def memoize[F[_], A](fa: F[A])(implicit F: Sync[F]): F[F[A]] = {
    import cats.implicits._
    for {
      ref <- Ref[F].of(fa.attempt)
      _   <- ref.update(_.flatTap(a => ref.set(a.pure[F])))
    } yield ref.get.flatten.rethrow
  }
} 
Example 176
Source File: AbstractHttpClient.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.cli.clients

import cats.effect.{Sync, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.cli.CliError.ClientError
import ch.epfl.bluebrain.nexus.cli.CliError.ClientError.{SerializationError, Unexpected}
import ch.epfl.bluebrain.nexus.cli.config.EnvConfig
import ch.epfl.bluebrain.nexus.cli.{logRetryErrors, ClientErrOr, Console}
import io.circe.Decoder
import org.http4s.circe.CirceEntityDecoder._
import org.http4s.client.Client
import org.http4s.{Request, Response}
import retry.CatsEffect._
import retry.RetryPolicy
import retry.syntax.all._

import scala.reflect.ClassTag
import scala.util.control.NonFatal

class AbstractHttpClient[F[_]: Timer](client: Client[F], env: EnvConfig)(implicit
    protected val F: Sync[F],
    protected val console: Console[F]
) {

  protected val retry                                = env.httpClient.retry
  protected def successCondition[A]                  = retry.condition.notRetryFromEither[A] _
  implicit protected val retryPolicy: RetryPolicy[F] = retry.retryPolicy
  implicit protected def logOnError[A]               = logRetryErrors[F, A]("interacting with an HTTP API")

  protected def executeDiscard[A](req: Request[F], returnValue: => A): F[ClientErrOr[A]] =
    execute(req, _.body.compile.drain.as(Right(returnValue)))

  protected def executeParse[A: Decoder](req: Request[F])(implicit A: ClassTag[A]): F[ClientErrOr[A]] =
    execute(
      req,
      _.attemptAs[A].value.map(
        _.leftMap(err =>
          SerializationError(err.message, s"The response payload was not of type '${A.runtimeClass.getSimpleName}'")
        )
      )
    )

  private def execute[A](req: Request[F], f: Response[F] => F[ClientErrOr[A]]): F[ClientErrOr[A]] =
    client
      .fetch(req)(ClientError.errorOr[F, A](r => f(r)))
      .recoverWith {
        case NonFatal(err) => F.delay(Left(Unexpected(Option(err.getMessage).getOrElse("").take(30))))
      }
      .retryingM(successCondition[A])
} 
Example 177
Source File: SparqlClient.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.cli.clients

import cats.effect.{Sync, Timer}
import ch.epfl.bluebrain.nexus.cli.config.EnvConfig
import ch.epfl.bluebrain.nexus.cli.sse.{OrgLabel, ProjectLabel}
import ch.epfl.bluebrain.nexus.cli.{ClientErrOr, Console}
import org.http4s._
import org.http4s.client.Client
import org.http4s.headers.`Content-Type`

trait SparqlClient[F[_]] {

  
  final def apply[F[_]: Sync: Timer](client: Client[F], env: EnvConfig, console: Console[F]): SparqlClient[F] = {
    implicit val c: Console[F] = console
    new LiveSparqlClient[F](client, env)
  }

  final val `application/sparql-query`: MediaType =
    new MediaType("application", "sparql-query")

  final private class LiveSparqlClient[F[_]: Timer: Console: Sync](client: Client[F], env: EnvConfig)
      extends AbstractHttpClient(client, env)
      with SparqlClient[F] {

    override def query(
        org: OrgLabel,
        proj: ProjectLabel,
        view: Option[Uri],
        queryStr: String
    ): F[ClientErrOr[SparqlResults]] = {
      val uri     = env.sparql(org, proj, view.getOrElse(env.defaultSparqlView))
      val headers = Headers(env.authorizationHeader.toList)
      val req     = Request[F](method = Method.POST, uri = uri, headers = headers)
        .withEntity(queryStr)
        .withContentType(`Content-Type`(`application/sparql-query`))
      executeParse[SparqlResults](req)
    }
  }
} 
Example 178
Source File: ProjectClient.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.cli.clients

import cats.effect.concurrent.Ref
import cats.effect.{Sync, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.cli.config.EnvConfig
import ch.epfl.bluebrain.nexus.cli.sse.{OrgLabel, OrgUuid, ProjectLabel, ProjectUuid}
import ch.epfl.bluebrain.nexus.cli.{ClientErrOr, Console}
import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder
import org.http4s.client.Client
import org.http4s.{Headers, Request}

trait ProjectClient[F[_]] {

  
  final def apply[F[_]: Sync: Timer](
      client: Client[F],
      env: EnvConfig,
      cache: Ref[F, Map[(OrgUuid, ProjectUuid), (OrgLabel, ProjectLabel)]],
      console: Console[F]
  ): ProjectClient[F] = {
    implicit val c: Console[F] = console
    new LiveProjectClient[F](client, env, cache)
  }

  private class LiveProjectClient[F[_]: Timer: Console: Sync](
      client: Client[F],
      env: EnvConfig,
      cache: Ref[F, Map[(OrgUuid, ProjectUuid), (OrgLabel, ProjectLabel)]]
  ) extends AbstractHttpClient[F](client, env)
      with ProjectClient[F] {

    override def labels(org: OrgUuid, proj: ProjectUuid): F[ClientErrOr[(OrgLabel, ProjectLabel)]] =
      cache.get.flatMap { map =>
        map.get((org, proj)) match {
          // value in cache, return
          case Some(value) => F.pure(Right(value))
          // value not in cache, fetch, update and return
          case None        =>
            get(org, proj).flatMap {
              // propagate error
              case l @ Left(_)      => F.pure(l)
              // success, update cache and return
              case r @ Right(value) =>
                cache.modify(m => (m.updated((org, proj), value), value)) *> F.pure(r)
            }
        }
      }

    private def get(org: OrgUuid, proj: ProjectUuid): F[ClientErrOr[(OrgLabel, ProjectLabel)]] = {
      val uri = env.project(org, proj)
      val req = Request[F](uri = uri, headers = Headers(env.authorizationHeader.toList))
      executeParse[NexusAPIProject](req).map {
        case Right(NexusAPIProject(orgLabel, projectLabel)) => Right((orgLabel, projectLabel))
        case Left(err)                                      => Left(err)
      }
    }
  }

  final private[ProjectClient] case class NexusAPIProject(`_organizationLabel`: OrgLabel, `_label`: ProjectLabel)
  private[ProjectClient] object NexusAPIProject {
    implicit val nexusAPIProjectDecoder: Decoder[NexusAPIProject] = deriveDecoder[NexusAPIProject]
  }
} 
Example 179
Source File: InfluxClient.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.cli.clients

import cats.effect.{Sync, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.cli._
import ch.epfl.bluebrain.nexus.cli.config.influx.InfluxConfig
import ch.epfl.bluebrain.nexus.cli.config.{AppConfig, EnvConfig}
import io.circe.Json
import org.http4s.client.Client
import org.http4s.{Method, Request, UrlForm}

trait InfluxClient[F[_]] {

  
  final def apply[F[_]: Sync: Timer](
      client: Client[F],
      config: AppConfig,
      console: Console[F]
  ): InfluxClient[F] = {
    implicit val c: Console[F] = console
    new LiveInfluxDbClient[F](client, config.influx, config.env)
  }
} 
Example 180
Source File: TestEventStreamClient.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.cli.dummies

import java.util.UUID

import cats.effect.Sync
import cats.effect.concurrent.Ref
import cats.implicits._
import ch.epfl.bluebrain.nexus.cli.clients.{EventStreamClient, ProjectClient}
import ch.epfl.bluebrain.nexus.cli.sse._
import ch.epfl.bluebrain.nexus.cli.{ClientErrOr, LabeledEvent}
import fs2.{Pipe, Stream}

class TestEventStreamClient[F[_]](events: List[Event], projectClient: ProjectClient[F])(implicit F: Sync[F])
    extends EventStreamClient[F] {

  private val noOffset: Offset                   = Offset(new UUID(0L, 0L))
  private val offsetEvents: Seq[(Offset, Event)] = events.map { ev =>
    (Offset(new UUID(ev.instant.toEpochMilli, 0L)), ev)
  }

  private def saveOffset(lastEventIdCache: Ref[F, Option[Offset]]): Pipe[F, (Offset, Event), Event] =
    _.evalMap { case (offset, event) => lastEventIdCache.update(_ => Some(offset)) >> F.pure(event) }

  private def eventsFrom(lastEventIdCache: Ref[F, Option[Offset]]): F[Seq[(Offset, Event)]]         =
    lastEventIdCache.get.map(lastEventId =>
      offsetEvents.dropWhile {
        case (offset, _) =>
          offset.value.getMostSignificantBits <= lastEventId.getOrElse(noOffset).value.getMostSignificantBits
      }
    )

  private def eventAndLabels(event: Event): F[ClientErrOr[LabeledEvent]] =
    projectClient.labels(event.organization, event.project).map(_.map { case (org, proj) => (event, org, proj) })

  override def apply(lastEventId: Option[Offset]): F[EventStream[F]]     =
    Ref.of(lastEventId).flatMap { ref =>
      val stream = eventsFrom(ref).map { events =>
        Stream.fromIterator[F](events.iterator).through(saveOffset(ref)).evalMap(eventAndLabels)
      }
      F.delay(EventStream(stream, ref))
    }

  override def apply(organization: OrgLabel, lastEventId: Option[Offset]): F[EventStream[F]] =
    Ref.of(lastEventId).flatMap { ref =>
      val stream = eventsFrom(ref).map { events =>
        Stream.fromIterator[F](events.iterator).through(saveOffset(ref)).evalMap(eventAndLabels).filter {
          case Right((_, org, _)) => org == organization
          case Left(_)            => true
        }
      }
      F.delay(EventStream(stream, ref))
    }

  override def apply(organization: OrgLabel, project: ProjectLabel, lastEventId: Option[Offset]): F[EventStream[F]] =
    Ref.of(lastEventId).flatMap { ref =>
      val stream = eventsFrom(ref).map { events =>
        Stream.fromIterator[F](events.iterator).through(saveOffset(ref)).evalMap(eventAndLabels).filter {
          case Right((_, org, proj)) => org == organization && proj == project
          case Left(_)               => true
        }
      }
      F.delay(EventStream(stream, ref))
    }
} 
Example 181
Source File: TestSparqlClient.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.cli.dummies

import java.io.ByteArrayOutputStream

import cats.effect.Sync
import cats.implicits._
import ch.epfl.bluebrain.nexus.cli.CliError.ClientError.SerializationError
import ch.epfl.bluebrain.nexus.cli.ClientErrOr
import ch.epfl.bluebrain.nexus.cli.clients.{SparqlClient, SparqlResults}
import ch.epfl.bluebrain.nexus.cli.sse.{Event, OrgLabel, ProjectLabel}
import io.circe.Json
import org.http4s.Uri

class TestSparqlClient[F[_]](events: List[Event])(implicit F: Sync[F]) extends SparqlClient[F] {

  import io.circe.parser._
  import org.apache.jena.query.{Dataset, DatasetFactory, QueryFactory, ReadWrite, ResultSetFormatter, _}
  import org.apache.jena.rdf.model.{Model, ModelFactory}
  import org.apache.jena.riot.system.StreamRDFLib
  import org.apache.jena.riot.{Lang, RDFParser}

  private def toJenaModel(j: Json): Model = {
    val model  = ModelFactory.createDefaultModel()
    val stream = StreamRDFLib.graph(model.getGraph)
    RDFParser.create.fromString(j.noSpaces).lang(Lang.JSONLD).parse(stream)
    model
  }

  val ds: Dataset = DatasetFactory.createTxnMem()
  events.foreach { event =>
    val jsonGraph = event.raw.hcursor.get[Json]("_source").getOrElse(Json.obj())
    val graphUri  = event.resourceId.addSegment("graph").renderString
    val model     = toJenaModel(jsonGraph)
    ds.begin(ReadWrite.WRITE)
    try {
      ds.removeNamedModel(graphUri)
      ds.commit()
    } finally {
      ds.end()
    }
    ds.begin(ReadWrite.WRITE)
    try {
      ds.addNamedModel(graphUri, model)
      ds.commit()
    } finally {
      ds.end()
    }
  }
  ds.setDefaultModel(ds.getUnionModel)

  override def query(
      org: OrgLabel,
      proj: ProjectLabel,
      view: Option[Uri],
      queryStr: String
  ): F[ClientErrOr[SparqlResults]] = {
    F.delay {
      ds.begin(ReadWrite.READ)
      try {
        val query   = QueryFactory.create(queryStr)
        val qexec   = QueryExecutionFactory.create(query, ds.asDatasetGraph())
        val results = qexec.execSelect

        val outputStream = new ByteArrayOutputStream()
        ResultSetFormatter.outputAsJSON(outputStream, results)
        val json         = new String(outputStream.toByteArray)
        decode[SparqlResults](json).leftMap(_ =>
          SerializationError("Unable to decode sparql results", classOf[SparqlResults].getSimpleName, Some(json))
        )
      } finally {
        ds.end()
      }
    }
  }
}

object TestSparqlClient {

  final def apply[F[_]](events: List[Event])(implicit F: Sync[F]): F[TestSparqlClient[F]] =
    F.delay(new TestSparqlClient[F](events))

} 
Example 182
Source File: package.scala    From pureconfig   with Mozilla Public License 2.0 5 votes vote down vote up
package pureconfig.module.catseffect

import scala.language.higherKinds
import scala.reflect.ClassTag

import cats.effect.{ Blocker, ContextShift, Sync }
import pureconfig.{ ConfigReader, ConfigSource, Derivation }
import pureconfig.module.catseffect

package object syntax {

  implicit class CatsEffectConfigSource(private val cs: ConfigSource) extends AnyVal {

    @inline
    final def loadF[F[_], A](blocker: Blocker)(implicit F: Sync[F], csf: ContextShift[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] =
      catseffect.loadF(cs, blocker)

    @deprecated("Use `cs.loadF[F, A](blocker)` instead", "0.12.3")
    def loadF[F[_], A](implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] =
      catseffect.loadF(cs)
  }
} 
Example 183
Source File: TestRoutes.scala    From scala-server-lambda   with MIT License 5 votes vote down vote up
package io.github.howardjohn.lambda.http4s

import cats.{Applicative, MonadError}
import cats.effect.Sync
import cats.implicits._
import io.github.howardjohn.lambda.LambdaHandlerBehavior
import io.github.howardjohn.lambda.LambdaHandlerBehavior._
import org.http4s.dsl.Http4sDsl
import org.http4s.{EntityDecoder, Header, HttpRoutes}
import org.http4s.circe._
import io.circe.generic.auto._
import io.circe.syntax._
import org.http4s.dsl.impl.OptionalQueryParamDecoderMatcher

class TestRoutes[F[_]] {

  object TimesQueryMatcher extends OptionalQueryParamDecoderMatcher[Int]("times")

  val dsl = Http4sDsl[F]

  import dsl._

  def routes(implicit sync: Sync[F],
             jsonDecoder: EntityDecoder[F, JsonBody],
             me: MonadError[F, Throwable],
             stringDecoder: EntityDecoder[F, String],
             ap: Applicative[F]): HttpRoutes[F] = HttpRoutes.of[F] {
    case GET -> Root / "hello" :? TimesQueryMatcher(times) =>
      Ok {
        Seq
          .fill(times.getOrElse(1))("Hello World!")
          .mkString(" ")
      }
    case GET -> Root / "long" => Applicative[F].pure(Thread.sleep(1000)).flatMap(_ => Ok("Hello World!"))
    case GET -> Root / "exception" => throw RouteException()
    case GET -> Root / "error" => InternalServerError()
    case req@GET -> Root / "header" =>
      val header = req.headers.find(h => h.name.value == inputHeader).map(_.value).getOrElse("Header Not Found")
      Ok(header, Header(outputHeader, outputHeaderValue))
    case req@POST -> Root / "post" => req.as[String].flatMap(s => Ok(s))
    case req@POST -> Root / "json" => req.as[JsonBody].flatMap(s => Ok(LambdaHandlerBehavior.jsonReturn.asJson))
  }

} 
Example 184
Source File: MongoClient.scala    From fs2-mongodb   with MIT License 5 votes vote down vote up
package org.lyranthe.fs2_mongodb

import com.mongodb.async.client.{MongoClient, MongoClients}
import com.mongodb.MongoClientSettings
import cats.effect.{Resource, Sync}

object Mongo {
  def fromUrl[F[_]](url: String)(implicit F: Sync[F]): Resource[F, MongoClient] =
    Resource.make(F.delay(MongoClients.create(url))){ client =>
      F.delay(client.close())
    }

  def fromSettings[F[_]](settings: MongoClientSettings)(
    implicit F: Sync[F]): Resource[F, MongoClient] = {
    Resource.make(F.delay(MongoClients.create(settings)))(client =>
      F.delay(client.close()))
  }
} 
Example 185
Source File: ProjectionFlow.scala    From ticket-booking-aecor   with Apache License 2.0 5 votes vote down vote up
package ru.pavkin.booking.common.view

import aecor.data.{Committable, EntityEvent}
import cats.effect.Sync
import io.chrisdavenport.log4cats.Logger
import cats.implicits._


object ProjectionFlow {

  def apply[F[_], K, E, S](log: Logger[F],
                           aggregateProjection: Projection[F, EntityEvent[K, E], S],
  )(implicit F: Sync[F]): fs2.Pipe[F, Committable[F, EntityEvent[K, E]], Unit] = {

    def foldEvent(event: EntityEvent[K, E], state: Option[S]): F[Option[S]] = {
      val newVersion = aggregateProjection.applyEvent(state)(event)
      log.debug(s"New version [$newVersion]") >>
        newVersion
          .fold(
            F.raiseError[Option[S]](
              new IllegalStateException(s"Projection failed for state = [$state], event = [$event]")
            )
          )(_.pure[F])
    }

    def runProjection(event: EntityEvent[K, E]): F[Unit] =
      for {
        (currentVersion, currentState) <- aggregateProjection.fetchVersionAndState(event)
        _ <- log.debug(s"Current $currentVersion [$currentState]")
        _ <- F.whenA(currentVersion.value < event.sequenceNr) {
              foldEvent(event, currentState).flatMap {
                case None => F.unit

                case Some(state) =>
                  aggregateProjection.saveNewVersion(state, currentVersion.next)
              }
            }
      } yield ()

    _.evalMap(_.traverse(runProjection)).evalMap(_.commit)
  }
} 
Example 186
Source File: StubConfirmationService.scala    From ticket-booking-aecor   with Apache License 2.0 5 votes vote down vote up
package ru.pavkin.booking.booking.service

import java.time.temporal.ChronoUnit
import java.time.{ Duration, Instant }
import java.util.concurrent.TimeUnit

import cats.Monad
import cats.data.NonEmptyList
import cats.effect.{ Clock, Sync }
import cats.effect.concurrent.Ref
import cats.implicits._
import ru.pavkin.booking.booking.service.TicketReservationService._
import ru.pavkin.booking.booking.service.StubConfirmationService.ConcertState
import ru.pavkin.booking.common.models._

class StubConfirmationService[F[_]: Monad](clock: Clock[F],
                                           state: Ref[F, Map[ConcertId, ConcertState]])
    extends TicketReservationService[F] {

  val expireAfter: Duration = Duration.of(6, ChronoUnit.HOURS)

  def reserve(bookingId: BookingKey,
           concertId: ConcertId,
           seats: NonEmptyList[Seat]): F[Either[ReservationFailure, Reservation]] =
    clock
      .realTime(TimeUnit.MILLISECONDS)
      .map(Instant.ofEpochMilli)
      .flatMap(
        now =>
          state.modify[Either[ReservationFailure, Reservation]](
            concerts =>
              concerts.get(concertId) match {
                case None => concerts -> Left(UnknownSeats)
                case Some(concertState) =>
                  concertState
                    .book(bookingId, seats)
                    .fold(e => concerts -> Left(e), {
                      case (c, t) =>
                        concerts.updated(concertId, c) -> Right(
                          Reservation(t, Some(now.plus(expireAfter)))
                        )
                    })

            }
        )
      )

  def release(bookingId: BookingKey): F[Either[ReleaseFailure, Unit]] =
    state.modify[Either[ReleaseFailure, Unit]](
      concerts =>
        Either
          .fromOption(concerts.find(_._2.bookedSeats.contains(bookingId)), UnknownBooking)
          .flatMap {
            case (concertId, concertState) =>
              concertState.release(bookingId).map(concertId -> _)
          } match {
          case Left(value)                  => concerts -> Left(value)
          case Right((concertId, newState)) => concerts.updated(concertId, newState) -> Right(())
      }
    )
}

object StubConfirmationService {

  def apply[F[_]: Sync](clock: Clock[F],
                        initial: Map[ConcertId, ConcertState]): F[StubConfirmationService[F]] =
    Ref.of(initial).map(new StubConfirmationService(clock, _))

  case class ConcertState(prices: Map[Seat, Money],
                          availableSeats: Set[Seat],
                          bookedSeats: Map[BookingKey, NonEmptyList[Seat]]) {

    def book(
      bookingId: BookingKey,
      seats: NonEmptyList[Seat]
    ): Either[ReservationFailure, (ConcertState, NonEmptyList[Ticket])] =
      if (bookedSeats.contains(bookingId)) Left(SeatsAlreadyBooked)
      else if (!seats.forall(availableSeats)) Left(SeatsAlreadyBooked)
      else if (!seats.forall(prices.contains)) Left(UnknownSeats)
      else
        Right(
          copy(
            availableSeats = availableSeats.diff(seats.toList.toSet),
            bookedSeats = bookedSeats.updated(bookingId, seats)
          ) -> seats.map(s => Ticket(s, prices(s)))
        )

    def release(bookingId: BookingKey): Either[ReleaseFailure, ConcertState] =
      bookedSeats.get(bookingId) match {
        case Some(booked) =>
          Right(
            copy(
              availableSeats = availableSeats ++ booked.toList.toSet,
              bookedSeats = bookedSeats - bookingId
            )
          )
        case None => Left(UnknownBooking)
      }
  }

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

import java.time.Instant

import cats.effect.Sync
import cats.implicits._
import ru.pavkin.booking.booking.booking.Bookings
import ru.pavkin.booking.booking.view.BookingViewRepository

class BookingExpirationProcess[F[_]: Sync](bookings: Bookings[F],
                                            bookingView: BookingViewRepository[F])
    extends (Instant => F[Unit]) {

  def apply(now: Instant): F[Unit] =
    bookingView
      .expired(now)
      .evalMap(k => bookings(k).expire.void)
      .compile
      .drain

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

import java.util.UUID

import cats.data.NonEmptyList
import cats.effect.Sync
import cats.implicits._
import ru.pavkin.booking.booking.booking.Bookings
import ru.pavkin.booking.booking.entity.{ BookingCommandRejection, BookingNotFound }
import ru.pavkin.booking.booking.view.{ BookingView, BookingViewRepository }
import ru.pavkin.booking.common.models.{ BookingKey, ClientId, ConcertId, Seat }

trait BookingEndpoint[F[_]] {
  def placeBooking(client: ClientId,
                   concertId: ConcertId,
                   seats: NonEmptyList[Seat]): F[Either[BookingCommandRejection, Unit]]

  def cancelBooking(clientId: ClientId,
                    bookingId: BookingKey,
                    reason: String): F[Either[BookingCommandRejection, Unit]]

  def clientBookings(client: ClientId): F[List[BookingView]]

}

final class DefaultBookingEndpoint[F[_]](
  bookings: Bookings[F],
  bookingsView: BookingViewRepository[F]
)(implicit F: Sync[F])
    extends BookingEndpoint[F] {

  def placeBooking(client: ClientId,
                   concertId: ConcertId,
                   seats: NonEmptyList[Seat]): F[Either[BookingCommandRejection, Unit]] =
    for {
      id <- Sync[F].delay(UUID.randomUUID())
      result <- bookings(BookingKey(id.toString)).place(client, concertId, seats)
    } yield result

  def cancelBooking(clientId: ClientId,
                    bookingId: BookingKey,
                    reason: String): F[Either[BookingCommandRejection, Unit]] =
    bookingsView.get(bookingId).flatMap {
      case None                               => F.pure(Left(BookingNotFound))
      case Some(b) if b.clientId =!= clientId => F.pure(Left(BookingNotFound))
      case Some(_)                            => bookings(bookingId).cancel(reason)
    }

  def clientBookings(client: ClientId): F[List[BookingView]] =
    bookingsView.byClient(client)
} 
Example 189
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 190
Source File: ConsoleLogger.scala    From odin   with Apache License 2.0 5 votes vote down vote up
package io.odin.loggers

import java.io.PrintStream

import cats.effect.{Sync, Timer}
import cats.syntax.all._
import io.odin.formatter.Formatter
import io.odin.{Level, Logger, LoggerMessage}

case class ConsoleLogger[F[_]: Timer](
    formatter: Formatter,
    out: PrintStream,
    err: PrintStream,
    override val minLevel: Level
)(implicit F: Sync[F])
    extends DefaultLogger[F](minLevel) {
  private def println(out: PrintStream, msg: LoggerMessage, formatter: Formatter): F[Unit] =
    F.delay(out.println(formatter.format(msg)))

  def log(msg: LoggerMessage): F[Unit] =
    if (msg.level < Level.Warn) {
      println(out, msg, formatter)
    } else {
      println(err, msg, formatter)
    }
}

object ConsoleLogger {
  def apply[F[_]: Timer: Sync](formatter: Formatter, minLevel: Level): Logger[F] =
    ConsoleLogger(formatter, scala.Console.out, scala.Console.err, minLevel)
} 
Example 191
Source File: FileLogger.scala    From odin   with Apache License 2.0 5 votes vote down vote up
package io.odin.loggers

import java.io.BufferedWriter
import java.nio.file.{Files, Paths}

import cats.effect.syntax.all._
import cats.effect.{Resource, Sync, Timer}
import cats.instances.list._
import cats.syntax.all._
import io.odin.formatter.Formatter
import io.odin.{Level, Logger, LoggerMessage}


case class FileLogger[F[_]: Timer](buffer: BufferedWriter, formatter: Formatter, override val minLevel: Level)(
    implicit F: Sync[F]
) extends DefaultLogger[F](minLevel) {
  def log(msg: LoggerMessage): F[Unit] =
    write(msg, formatter).guarantee(flush)

  override def log(msgs: List[LoggerMessage]): F[Unit] =
    msgs.traverse(write(_, formatter)).void.guarantee(flush)

  private def write(msg: LoggerMessage, formatter: Formatter): F[Unit] =
    F.delay {
      buffer.write(formatter.format(msg) + System.lineSeparator())
    }

  private def flush: F[Unit] = F.delay(buffer.flush()).handleErrorWith(_ => F.unit)
}

object FileLogger {
  def apply[F[_]: Timer](fileName: String, formatter: Formatter, minLevel: Level)(
      implicit F: Sync[F]
  ): Resource[F, Logger[F]] = {
    def mkBuffer: F[BufferedWriter] = F.delay(Files.newBufferedWriter(Paths.get(fileName)))
    def closeBuffer(buffer: BufferedWriter): F[Unit] =
      F.delay(buffer.close()).handleErrorWith(_ => F.unit)

    Resource.make(mkBuffer)(closeBuffer).map { buffer =>
      FileLogger(buffer, formatter, minLevel)
    }
  }
} 
Example 192
Source File: ProducerRecord.scala    From skafka   with MIT License 5 votes vote down vote up
package com.evolutiongaming.skafka.producer

import java.time.Instant

import cats.effect.Sync
import cats.implicits._
import com.evolutiongaming.skafka._

final case class ProducerRecord[+K, +V](
  topic: Topic,
  value: Option[V] = None,
  key: Option[K] = None,
  partition: Option[Partition] = None,
  timestamp: Option[Instant] = None,
  headers: List[Header] = Nil)

object ProducerRecord {

  def apply[K, V](topic: Topic, value: V, key: K): ProducerRecord[K, V] = {
    ProducerRecord(topic = topic, value = Some(value), key = Some(key))
  }


  implicit class ProducerRecordOps[K, V](val self: ProducerRecord[K, V]) extends AnyVal {

    def toBytes[F[_] : Sync](implicit
      toBytesK: ToBytes[F, K],
      toBytesV: ToBytes[F, V]
    ): F[ProducerRecord[Bytes, Bytes]] = {
      val topic = self.topic

      for {
        k <- self.key.traverse { toBytesK(_, topic) }
        v <- self.value.traverse { toBytesV(_, topic) }
      } yield {
        self.copy(value = v, key = k)
      }
    }
  }
} 
Example 193
Source File: AccountRepositoryInMemory.scala    From frdomain-extras   with Apache License 2.0 5 votes vote down vote up
package frdomain.ch6
package domain
package repository
package interpreter

import java.time.LocalDateTime
import scala.collection.immutable.Map 

import cats._
import cats.data._
import cats.implicits._
import cats.instances.all._
import cats.effect.concurrent.Ref
import cats.effect.Sync

import common._
import model.account._

// Constructor private for the interpreter to prevent the Ref from leaking
// access through smart constructor below
final class AccountRepositoryInMemory[M[_]: Monad] private (repo: Ref[M, Map[AccountNo, Account]])
  extends AccountRepository[M] {

  def query(no: AccountNo): M[Option[Account]] = repo.get.map(_.get(no))  

  def store(a: Account): M[Account] = repo.update(_ + ((a.no, a))).map(_ => a)

  def query(openedOn: LocalDateTime): M[List[Account]] = 
    repo.get.map(_.values.filter(_.dateOfOpen.getOrElse(today) == openedOn).toList)

  def all: M[List[Account]] = repo.get.map(_.values.toList)

  def balance(no: AccountNo): M[Option[Balance]] = query(no).map(_.map(_.balance))
}

// Smart constructor 
object AccountRepositoryInMemory {
  def make[M[_]: Sync]: M[AccountRepositoryInMemory[M]] =
    Ref.of[M, Map[AccountNo, Account]](Map.empty).map(new AccountRepositoryInMemory(_))
} 
Example 194
Source File: HostnamePlatform.scala    From ip4s   with Apache License 2.0 5 votes vote down vote up
package com.comcast.ip4s

import java.net.{InetAddress, UnknownHostException}

import cats.data.NonEmptyList
import cats.effect.Sync

private[ip4s] trait HostnamePlatform { self: Hostname =>

  
  def resolveAll[F[_]: Sync]: F[Option[NonEmptyList[IpAddress]]] =
    Sync[F].delay {
      try {
        val addrs = InetAddress.getAllByName(self.toString)
        NonEmptyList.fromList(addrs.toList.flatMap(addr => IpAddress.fromBytes(addr.getAddress)))
      } catch {
        case _: UnknownHostException => None
      }
    }
} 
Example 195
Source File: CatsEffect.scala    From cats-effect-testing   with Apache License 2.0 5 votes vote down vote up
package cats.effect.testing.specs2

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

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

trait CatsEffect {

  protected val Timeout: Duration = 10.seconds

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

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

import cats.data.EitherT
import cats.effect.testing.scalatest.AsyncIOSpec
import cats.effect.{IO, Sync}
import org.scalatest.matchers.should.Matchers
import org.scalatest.freespec.AsyncFreeSpec
import org.scalatestplus.scalacheck.{CheckerAsserting, ScalaCheckPropertyChecks}

class IOTest extends AsyncFreeSpec with AsyncIOSpec with Matchers with ScalaCheckPropertyChecks {

  "Scalacheck IO assertions" - {

    "Assert success" in {
      forAll { (l1: List[Int], l2: List[Int]) =>
        IO.delay(l1.size + l2.size shouldBe (l1 ::: l2).size)
      }
    }

    "Assert exception" in {
      val check: IO[Unit] = forAll { (l1: List[Int], l2: List[Int]) =>
        IO.delay(l1.size + l2.size shouldBe -1)
      }

      check.assertThrows[Exception]
    }

    implicit def ioCheckingAsserting[A]: CheckerAsserting[IO[A]] { type Result = IO[Unit] } =
      new EffectCheckerAsserting

  }

  "Scalacheck EitherT[IO, Throwable, A] assertions" - {

    type Eff[A] = EitherT[IO, Throwable, A]

    "Assert success" in {
      val check = forAll { (l1: List[Int], l2: List[Int]) =>
        Sync[Eff].delay(l1.size + l2.size shouldBe (l1 ::: l2).size)
      }

      check.leftSemiflatMap[Unit](IO.raiseError).merge.assertNoException
    }

    "Assert exception" in {
      val check = forAll { (l1: List[Int], l2: List[Int]) =>
        Sync[Eff].delay(l1.size + l2.size shouldBe -1)
      }

      check.leftSemiflatMap[Unit](IO.raiseError[Unit]).merge.assertThrows[Exception]
    }

    implicit def checkingAsserting[A]: CheckerAsserting[Eff[A]] { type Result = Eff[Unit] } =
      new EffectCheckerAsserting
  }

} 
Example 197
Source File: AssertingSyntax.scala    From cats-effect-testing   with Apache License 2.0 5 votes vote down vote up
package cats.effect.testing.scalatest

import cats.Functor
import cats.effect.Sync
import org.scalatest.{Assertion, Assertions, Succeeded}
import cats.implicits._


    def assertThrows[E <: Throwable](implicit F: Sync[F], ct: reflect.ClassTag[E]): F[Assertion] =
      self.attempt.flatMap {
        case Left(t: E) => F.pure(Succeeded: Assertion)
        case Left(t) =>
          F.delay(
            fail(
              s"Expected an exception of type ${ct.runtimeClass.getName} but got an exception: $t"
            )
          )
        case Right(a) =>
          F.delay(
            fail(s"Expected an exception of type ${ct.runtimeClass.getName} but got a result: $a")
          )
      }
  }
} 
Example 198
Source File: TransactionRoute.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.example.transaction

import java.util.UUID

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


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

object TransactionRoute {

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

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

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

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

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

} 
Example 199
Source File: Supervision.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.kafkadistributedprocessing

import cats.effect.{ Sync, Timer }
import fs2.Stream.retry
import scala.concurrent.duration._

object Supervision {
  type Supervision[F[_]] = F[Unit] => F[Unit]
  def exponentialBackoff[F[_]: Timer: Sync](minBackoff: FiniteDuration = 2.seconds,
                                            maxBackoff: FiniteDuration = 10.seconds,
                                            randomFactor: Double = 0.2,
                                            maxAttempts: Int = Int.MaxValue): Supervision[F] = {
    def nextDelay(in: FiniteDuration): FiniteDuration =
      FiniteDuration((in.toMillis * (1 + randomFactor)).toLong, MILLISECONDS).min(maxBackoff)
    fa =>
      retry(fa, minBackoff, nextDelay, maxAttempts, Function.const(true)).compile.drain
  }
  def noop[F[_]]: Supervision[F] = identity
} 
Example 200
Source File: E2eSupport.scala    From aecor   with MIT License 5 votes vote down vote up
package aecor.testkit

import aecor.data.{ EventsourcedBehavior, _ }
import aecor.runtime.{ EventJournal, Eventsourced }
import cats.data.StateT
import cats.effect.{ IO, Sync }
import cats.implicits._
import cats.mtl.MonadState
import cats.tagless.FunctorK
import cats.tagless.syntax.functorK._
import cats.~>
import fs2.Stream
import monocle.Lens

import scala.collection.immutable._

object E2eSupport {

  final class Runtime[F[_]] {
    def deploy[M[_[_]]: FunctorK, S, E, K](
      behavior: EventsourcedBehavior[M, F, S, E],
      journal: EventJournal[F, K, E]
    )(implicit F: Sync[F]): K => M[F] =
      Eventsourced[M, F, S, E, K](behavior, journal)
  }

  abstract class Processes[F[_]](items: Vector[F[Unit]]) {
    protected type S
    protected implicit def F: MonadState[F, S]

    final private implicit val monad = F.monad

    final def runProcesses: F[Unit] =
      for {
        stateBefore <- F.get
        _ <- items.sequence
        stateAfter <- F.get
        _ <- if (stateAfter == stateBefore) {
              ().pure[F]
            } else {
              runProcesses
            }
      } yield ()

    final def wireK[I, M[_[_]]: FunctorK](behavior: I => M[F]): I => M[F] =
      i =>
        behavior(i).mapK(new (F ~> F) {
          override def apply[A](fa: F[A]): F[A] =
            fa <* runProcesses
        })

    final def wire[A, B](f: F[B]): F[B] =
      f <* runProcesses
  }

  object Processes {
    def apply[F[_], S0](items: F[Unit]*)(implicit F0: MonadState[F, S0]): Processes[F] =
      new Processes[F](items.toVector) {
        final override type S = S0
        override implicit def F: MonadState[F, S] = F0
      }
  }
}

trait E2eSupport {
  import cats.mtl.instances.state._

  type SpecState

  type F[A] = StateT[IO, SpecState, A]

  final def mkJournal[I, E](lens: Lens[SpecState, StateEventJournal.State[I, E]],
                            tagging: Tagging[I]): StateEventJournal[F, I, SpecState, E] =
    StateEventJournal[F, I, SpecState, E](lens, tagging)

  final def wireProcess[In](process: In => F[Unit],
                            source: Stream[F, Committable[F, In]],
                            sources: Stream[F, Committable[F, In]]*)(implicit F: Sync[F]): F[Unit] =
    sources
      .fold(source)(_ ++ _)
      .evalMap(_.process(process))
      .compile
      .drain

  val runtime = new E2eSupport.Runtime[F]
}