com.twitter.util.Future Scala Examples

The following examples show how to use com.twitter.util.Future. 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: CirceSpec.scala    From featherbed   with Apache License 2.0 6 votes vote down vote up
package featherbed.circe

import cats.implicits._
import com.twitter.util.Future
import io.circe._
import io.circe.generic.auto._
import io.circe.parser.parse
import io.circe.syntax._
import org.scalatest.FlatSpec
import shapeless.{Coproduct, Witness}
import shapeless.union.Union

case class Foo(someText: String, someInt: Int)

class CirceSpec extends FlatSpec {

  "post request of a case class" should "derive JSON encoder" in {

    import com.twitter.util.{Future, Await}
    import com.twitter.finagle.{Service, Http}
    import com.twitter.finagle.http.{Request, Response}
    import java.net.InetSocketAddress

    val server = Http.serve(new InetSocketAddress(8766), new Service[Request, Response] {
      def apply(request: Request): Future[Response] = Future {
        val rep = Response()
        rep.contentString = s"${request.contentString}"
        rep.setContentTypeJson()
        rep
      }
    })

    import java.net.URL
    val client = new featherbed.Client(new URL("http://localhost:8766/api/"))

    import io.circe.generic.auto._

    val req = client.post("foo/bar")
      .withContent(Foo("Hello world!", 42), "application/json")
        .accept[Coproduct.`"application/json"`.T]

    val result = Await.result {
       req.send[Foo]()
    }

    Foo("test", 42).asJson.toString

    parse("""{"someText": "test", "someInt": 42}""").toValidated.map(_.as[Foo])

    Await.result(server.close())

  }

  "API example" should "compile" in {
    import shapeless.Coproduct
    import java.net.URL
    import com.twitter.util.Await
    case class Post(userId: Int, id: Int, title: String, body: String)

    case class Comment(postId: Int, id: Int, name: String, email: String, body: String)
    class JSONPlaceholderAPI(baseUrl: URL) {

      private val client = new featherbed.Client(baseUrl)
      type JSON = Coproduct.`"application/json"`.T

      object posts {

        private val listRequest = client.get("posts").accept[JSON]
        private val getRequest = (id: Int) => client.get(s"posts/$id").accept[JSON]

        def list(): Future[Seq[Post]] = listRequest.send[Seq[Post]]()
        def get(id: Int): Future[Post] = getRequest(id).send[Post]()
      }

      object comments {
        private val listRequest = client.get("comments").accept[JSON]
        private val getRequest = (id: Int) => client.get(s"comments/$id").accept[JSON]

        def list(): Future[Seq[Comment]] = listRequest.send[Seq[Comment]]()
        def get(id: Int): Future[Comment] = getRequest(id).send[Comment]()
      }
    }

    val apiClient = new JSONPlaceholderAPI(new URL("http://jsonplaceholder.typicode.com/"))

    Await.result(apiClient.posts.list())
  }

} 
Example 2
Source File: ExceptionTranslationFilter.scala    From finatra-activator-thrift-seed   with Apache License 2.0 5 votes vote down vote up
package com.example

import com.twitter.finagle.{Service, TimeoutException}
import com.twitter.finatra.thrift.thriftscala.ClientErrorCause.RequestTimeout
import com.twitter.finatra.thrift.thriftscala.ServerErrorCause.InternalServerError
import com.twitter.finatra.thrift.thriftscala.{ClientError, ServerError}
import com.twitter.finatra.thrift.{ThriftFilter, ThriftRequest}
import com.twitter.inject.Logging
import com.twitter.util.{Future, NonFatal}
import javax.inject.Singleton

@Singleton
class ExceptionTranslationFilter
  extends ThriftFilter
  with Logging {

  override def apply[T, U](request: ThriftRequest[T], service: Service[ThriftRequest[T], U]): Future[U] = {
    service(request).rescue {
      case e: TimeoutException =>
        Future.exception(
          ClientError(RequestTimeout, e.getMessage))
      case e: ClientError =>
        Future.exception(e)
      case NonFatal(e) =>
        error("Unhandled exception", e)
        Future.exception(
          ServerError(InternalServerError, e.getMessage))
    }
  }
} 
Example 3
Source File: Client.scala    From roc   with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
package roc
package postgresql

import com.twitter.finagle._
import com.twitter.finagle.ServiceFactory
import com.twitter.util.{Closable, Future, Time}

object Client {
  def apply (factory: ServiceFactory[Request, Result]): Client = 
    new StdClient(factory)
}

trait Client extends Closable {
  def query(req: Request): Future[Result]
}

final class StdClient(val factory: ServiceFactory[Request, Result]) extends Client {
  private[this] val service = factory.toService

  def query(req: Request): Future[Result] = service(req)

  def close(deadline: Time): Future[Unit] = service.close(deadline)
} 
Example 4
Source File: Query.scala    From finagle-postgres   with Apache License 2.0 5 votes vote down vote up
package com.twitter.finagle.postgres.generic

import com.twitter.concurrent.AsyncStream

import scala.collection.immutable.Queue
import com.twitter.finagle.postgres.{Param, PostgresClient, Row}
import com.twitter.util.Future

import scala.language.existentials

case class Query[T](parts: Seq[String], queryParams: Seq[QueryParam], cont: Row => T) {

  def stream(client: PostgresClient): AsyncStream[T] = {
    val (queryString, params) = impl
    client.prepareAndQueryToStream[T](queryString, params: _*)(cont)
  }

  def run(client: PostgresClient): Future[Seq[T]] =
    stream(client).toSeq

  def exec(client: PostgresClient): Future[Int] = {
    val (queryString, params) = impl
    client.prepareAndExecute(queryString, params: _*)
  }

  def map[U](fn: T => U): Query[U] = copy(cont = cont andThen fn)

  def as[U](implicit rowDecoder: RowDecoder[U], columnNamer: ColumnNamer): Query[U] = {
    copy(cont = row => rowDecoder(row)(columnNamer))
  }

  private def impl: (String, Seq[Param[_]]) = {
    val (last, placeholders, params) = queryParams.foldLeft((1, Queue.empty[Seq[String]], Queue.empty[Param[_]])) {
      case ((start, placeholders, params), next) =>
        val nextPlaceholders = next.placeholders(start)
        val nextParams = Queue(next.params: _*)
        (start + nextParams.length, placeholders enqueue nextPlaceholders, params ++ nextParams)
    }

    val queryString = parts.zipAll(placeholders, "", Seq.empty).flatMap {
      case (part, ph) => Seq(part, ph.mkString(", "))
    }.mkString

    (queryString, params)
  }


}

object Query {
  implicit class RowQueryOps(val self: Query[Row]) extends AnyVal {
    def ++(that: Query[Row]): Query[Row] = Query[Row](
      parts = if(self.parts.length > self.queryParams.length)
        (self.parts.dropRight(1) :+ (self.parts.lastOption.getOrElse("") + that.parts.headOption.getOrElse(""))) ++ that.parts.drop(1)
      else
        self.parts ++ that.parts,
      queryParams = self.queryParams ++ that.queryParams,
      cont = self.cont
    )

    def ++(that: String): Query[Row] = Query[Row](
      parts = if(self.parts.length > self.queryParams.length)
          self.parts.dropRight(1) :+ (self.parts.last + that)
        else
          self.parts :+ that,
      queryParams = self.queryParams,
      cont = self.cont
    )
  }
} 
Example 5
Source File: PreparedStatement.scala    From finagle-postgres   with Apache License 2.0 5 votes vote down vote up
package com.twitter.finagle.postgres

import com.twitter.concurrent.AsyncStream
import com.twitter.finagle.postgres.codec.Errors
import com.twitter.util.Future


trait PreparedStatement {
  def fire(params: Param[_]*): Future[QueryResponse]

  def exec(params: Param[_]*): Future[OK] = fire(params: _*) flatMap {
    case ok @ OK(_)   => Future.value(ok)
    case ResultSet(_) => Future.exception(Errors.client("Update query expected"))
  }

  def selectToStream[T](params: Param[_]*)(f: Row => T): Future[AsyncStream[T]] = fire(params: _*) map {
    case ResultSet(rows) => rows.map(f)
    case OK(_) => AsyncStream.empty
  }
  def select[T](params: Param[_]*)(f: Row => T): Future[Seq[T]] =
    selectToStream(params: _*)(f).flatMap(_.toSeq)

  def selectFirst[T](params: Param[_]*)(f: Row => T): Future[Option[T]] =
    select[T](params:_*)(f) flatMap { rows => Future.value(rows.headOption) }
} 
Example 6
Source File: PostgresClient.scala    From finagle-postgres   with Apache License 2.0 5 votes vote down vote up
package com.twitter.finagle.postgres
import java.nio.charset.Charset

import com.twitter.concurrent.AsyncStream
import com.twitter.finagle.Status
import com.twitter.finagle.postgres.messages.SelectResult
import com.twitter.finagle.postgres.values.Types
import com.twitter.util.Future

trait PostgresClient {

  def charset: Charset

  
  def isAvailable: Boolean
}


object PostgresClient {

  case class TypeSpecifier(receiveFunction: String, typeName: String, elemOid: Long = 0)

  val defaultTypes = Map(
    Types.BOOL -> TypeSpecifier("boolrecv", "bool"),
    Types.BYTE_A -> TypeSpecifier("bytearecv", "bytea"),
    Types.CHAR -> TypeSpecifier("charrecv", "char"),
    Types.NAME -> TypeSpecifier("namerecv", "name"),
    Types.INT_8 -> TypeSpecifier("int8recv", "int8"),
    Types.INT_2 -> TypeSpecifier("int2recv", "int2"),
    Types.INT_4 -> TypeSpecifier("int4recv", "int4"),
    Types.REG_PROC -> TypeSpecifier("regprocrecv", "regproc"),
    Types.TEXT -> TypeSpecifier("textrecv", "text"),
    Types.OID -> TypeSpecifier("oidrecv", "oid"),
    Types.TID -> TypeSpecifier("tidrecv", "tid"),
    Types.XID -> TypeSpecifier("xidrecv", "xid"),
    Types.CID -> TypeSpecifier("cidrecv", "cid"),
    Types.JSON -> TypeSpecifier("json_recv", "json"),
    Types.XML -> TypeSpecifier("xml_recv", "xml"),
    Types.POINT -> TypeSpecifier("point_recv", "point"),
    Types.L_SEG -> TypeSpecifier("lseg_recv", "lseg"),
    Types.PATH -> TypeSpecifier("path_recv", "path"),
    Types.BOX -> TypeSpecifier("box_recv", "box"),
    Types.POLYGON -> TypeSpecifier("poly_recv", "poly"),
    Types.LINE -> TypeSpecifier("line_recv", "line"),
    Types.CIDR -> TypeSpecifier("cidr_recv", "cidr"),
    Types.FLOAT_4 -> TypeSpecifier("float4recv", "float4"),
    Types.FLOAT_8 -> TypeSpecifier("float8recv", "float8"),
    Types.ABS_TIME -> TypeSpecifier("abstimerecv", "abstime"),
    Types.REL_TIME -> TypeSpecifier("reltimerecv", "reltime"),
    Types.T_INTERVAL -> TypeSpecifier("tinternalrecv", "tinternal"),
    Types.UNKNOWN -> TypeSpecifier("unknownrecv", "unknown"),
    Types.CIRCLE -> TypeSpecifier("circle_recv", "circle"),
    Types.MONEY -> TypeSpecifier("cash_recv", "cash"),
    Types.MAC_ADDR -> TypeSpecifier("macaddr_recv", "macaddr"),
    Types.INET -> TypeSpecifier("inet_recv", "inet"),
    Types.BP_CHAR -> TypeSpecifier("bpcharrecv", "bpchar"),
    Types.VAR_CHAR -> TypeSpecifier("varcharrecv", "varchar"),
    Types.DATE -> TypeSpecifier("date_recv", "date"),
    Types.TIME -> TypeSpecifier("time_recv", "time"),
    Types.TIMESTAMP -> TypeSpecifier("timestamp_recv", "timestamp"),
    Types.TIMESTAMP_TZ -> TypeSpecifier("timestamptz_recv", "timestamptz"),
    Types.INTERVAL -> TypeSpecifier("interval_recv", "interval"),
    Types.TIME_TZ -> TypeSpecifier("timetz_recv", "timetz"),
    Types.BIT -> TypeSpecifier("bit_recv", "bit"),
    Types.VAR_BIT -> TypeSpecifier("varbit_recv", "varbit"),
    Types.NUMERIC -> TypeSpecifier("numeric_recv", "numeric"),
    Types.RECORD -> TypeSpecifier("record_recv", "record"),
    Types.VOID -> TypeSpecifier("void_recv", "void"),
    Types.UUID -> TypeSpecifier("uuid_recv", "uuid")
  )

} 
Example 7
Source File: NumericSpec.scala    From finagle-postgres   with Apache License 2.0 5 votes vote down vote up
package com.twitter.finagle.postgres.integration

import com.twitter.finagle.Postgres
import com.twitter.finagle.postgres._
import com.twitter.util.{Await, Future}
import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks

class NumericSpec extends Spec with ScalaCheckDrivenPropertyChecks {


  for {
    hostPort <- sys.env.get("PG_HOST_PORT")
    user <- sys.env.get("PG_USER")
    password = sys.env.get("PG_PASSWORD")
    dbname <- sys.env.get("PG_DBNAME")
    useSsl = sys.env.getOrElse("USE_PG_SSL", "0") == "1"
  } yield {

    val binaryClient = Postgres.Client()
      .database(dbname)
      .withCredentials(user, password)
      .withBinaryParams(true)
      .withBinaryResults(true)
      .newRichClient(hostPort)

    val textClient = Postgres.Client()
      .database(dbname)
      .withCredentials(user, password)
      .withBinaryParams(false)
      .withBinaryResults(false)
      .newRichClient(hostPort)

    Await.result((textClient.query(
      """
        |DROP TABLE IF EXISTS numeric_test;
        |CREATE TABLE numeric_test(d DECIMAL NOT NULL);
      """.stripMargin)))

    def rowsOf(qr: QueryResponse): Future[Seq[Row]] = qr match {
      case OK(_) => Future.value(Seq.empty)
      case ResultSet(rs) => rs.toSeq
    }

    def testBinaryEncode(in: BigDecimal) = Await.result {
      for {
        _ <- binaryClient.execute("DELETE FROM numeric_test")
        _ <- binaryClient.prepareAndExecute("INSERT INTO numeric_test VALUES($1)", Param(in))
        r <- textClient.query("SELECT * FROM numeric_test")
        rows <- rowsOf(r)
      } yield rows.map(_.get[BigDecimal](0)) must equal(Seq(in))
    }

    def testBinaryDecode(in: BigDecimal) = Await.result {
      for {
        _ <- textClient.execute("DELETE FROM numeric_test")
        _ <- textClient.prepareAndExecute("INSERT INTO numeric_test VALUES($1)", Param(in))
        r <- binaryClient.query("SELECT * FROM numeric_test")
        rows <- rowsOf(r)
      } yield rows.map(_.get[BigDecimal](0)) must equal(Seq(in))
    }

    "Binary client" should {
      "encode decimal agree with text client" in forAll { in: BigDecimal =>
        testBinaryEncode(in)
      }
      "decode decimal agree with text client" in forAll { in: BigDecimal =>
        testBinaryDecode(in)
      }
    }

  }
} 
Example 8
Source File: TransactionSpec.scala    From finagle-postgres   with Apache License 2.0 5 votes vote down vote up
package com.twitter.finagle.postgres.integration

import com.twitter.finagle.Postgres
import com.twitter.finagle.postgres.Spec
import com.twitter.util.{Await, Future}

class TransactionSpec extends Spec {
  for {
    hostPort <- sys.env.get("PG_HOST_PORT")
    user <- sys.env.get("PG_USER")
    password = sys.env.get("PG_PASSWORD")
    dbname <- sys.env.get("PG_DBNAME")
    useSsl = sys.env.getOrElse("USE_PG_SSL", "0") == "1"
  } yield {

    val client = Postgres.Client()
      .withCredentials(user, password)
      .database(dbname)
      .conditionally(useSsl, _.withTransport.tlsWithoutValidation)
      .newRichClient(hostPort)

    Await.result(client.query(
      """
        |DROP TABLE IF EXISTS transaction_test;
        |CREATE TABLE transaction_test(id integer primary key);
      """.stripMargin))

    "A postgres transaction" should {

      "commit if the transaction future is successful" in {
        Await.result {
          client.inTransaction {
            c => for {
              _ <- c.prepareAndExecute("DELETE FROM transaction_test")
              _ <- c.prepareAndExecute("INSERT INTO transaction_test VALUES(1)")
              _ <- c.prepareAndExecute("INSERT INTO transaction_test VALUES(2)")
            } yield ()
          }
        }
        val count = Await.result(client.prepareAndQuery("SELECT COUNT(*)::int4 AS count FROM transaction_test WHERE id IN (1,2)") {
          row => row.get[Int]("count")
        }.map(_.head))
        assert(count == 2)
      }

      "rollback the transaction if the transaction future fails" in {
        val failed = client.inTransaction {
          c => for {
            _ <- c.prepareAndExecute("DELETE FROM transaction_test")
            _ <- c.prepareAndExecute("INSERT INTO transaction_test VALUES(3)")
            _ <- c.prepareAndExecute("INSERT INTO transaction_test VALUES(4)")
            _ <- Future.exception(new Exception("Roll it back!"))
            _ <- c.prepareAndExecute("INSERT INTO transaction_test VALUES(5)")
          } yield ()
        }.liftToTry

        val failedResult = Await.result(failed)

        val inTable = Await.result(client.prepareAndQuery("SELECT * FROM transaction_test") {
          row => row.get[Int]("id")
        }).toList.sorted
        assert(inTable == List(1, 2))
      }

    }
  }
} 
Example 9
Source File: ConstFuture.scala    From arrows   with Apache License 2.0 5 votes vote down vote up
package arrows.twitter

import com.twitter.util.Future
import com.twitter.util.Try
import com.twitter.util.Awaitable
import com.twitter.util.Duration
import com.twitter.util.Return
import scala.runtime.NonLocalReturnControl
import com.twitter.concurrent.Scheduler
import com.twitter.util.FutureNonLocalReturnControl
import com.twitter.util.Local
import com.twitter.util.Monitor
import scala.util.control.NonFatal
import com.twitter.util.Promise
import com.twitter.util.Throw

trait ConstFuture[T] extends Future[T] {
  final def isReady(implicit permit: Awaitable.CanAwait): Boolean = true

  override final def ready(timeout: Duration)(implicit permit: Awaitable.CanAwait) = this
  final def poll: Option[com.twitter.util.Try[T]] = Some(toTry)

  protected def toTry: Try[T]

  final def respond(k: Try[T] => Unit): Future[T] = {
    val saved = Local.save()
    Scheduler.submit(new Runnable {
      def run(): Unit = {
        val current = Local.save()
        Local.restore(saved)
        try k(toTry)
        catch Monitor.catcher
        finally Local.restore(current)
      }
    })
    this
  }

  final def raise(interrupt: Throwable): Unit = ()

  final def transform[B](f: Try[T] => Future[B]): Future[B] = {
    val p = new Promise[B]
    // see the note on `respond` for an explanation of why `Scheduler` is used.
    val saved = Local.save()
    Scheduler.submit(new Runnable {
      def run(): Unit = {
        val current = Local.save()
        Local.restore(saved)
        val computed = try f(toTry)
        catch {
          case e: NonLocalReturnControl[_] => Future.exception(new FutureNonLocalReturnControl(e))
          case NonFatal(e)                 => Future.exception(e)
          case t: Throwable =>
            Monitor.handle(t)
            throw t
        } finally Local.restore(current)
        p.become(computed)
      }
    })
    p
  }
}

class ReturnFuture[T](r: T) extends ConstFuture[T] {
  override final def result(timeout: Duration)(implicit permit: Awaitable.CanAwait): T = r
  override final def toTry = Return(r)
}

class ThrowFuture[T](ex: Throwable) extends ConstFuture[T] {
  override final def result(timeout: Duration)(implicit permit: Awaitable.CanAwait): T = throw ex
  override final def toTry = Throw(ex)
} 
Example 10
Source File: TwitterFuture.scala    From arrows   with Apache License 2.0 5 votes vote down vote up
package benchmarks

import com.twitter.util.Promise
import com.twitter.util.Future
import org.openjdk.jmh.annotations.Benchmark
import scala.util.Try

trait TwitterFuture {
  this: Benchmarks =>

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

  @Benchmark
  def twitterFuture = {
    import com.twitter.util.Await
    Try(Await.result(gen(1)))
  }
}

object TwitterFutureGen extends Gen[Int => Future[Int]] {

  def sync = Future.value _

  def async(schedule: Runnable => Unit) = {
    v =>
      val p = Promise.apply[Int]()
      schedule(() => p.setValue(v))
      p
  }

  def failure(ex: Throwable) = _ => Future.exception(ex)

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

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

  def handle(t: Int => Future[Int], i: Int) =
    t.andThen(_.handle { case _ => i })
} 
Example 11
Source File: OAuth2Spec.scala    From finch-oauth2   with Apache License 2.0 5 votes vote down vote up
package io.finch.oauth2

import cats.effect.IO
import com.twitter.finagle.http.Status
import com.twitter.finagle.oauth2._
import com.twitter.util.Future
import io.finch._
import io.finch.catsEffect._
import org.mockito.Mockito._
import org.scalatest.{FlatSpec, Matchers}
import org.scalatest.mockito.MockitoSugar
import org.scalatest.prop.Checkers

class OAuth2Spec extends FlatSpec with Matchers with Checkers with MockitoSugar {

  behavior of "OAuth2"

  it should "authorize the requests" in {
    val at: AccessToken = AccessToken("foo", None, None, None, null)
    val dh: DataHandler[Int] = mock[DataHandler[Int]]
    val ai: AuthInfo[Int] = new AuthInfo(42, "foo", None, None)

    when(dh.findAccessToken("bar")).thenReturn(Future.value(Some(at)))
    when(dh.isAccessTokenExpired(at)).thenReturn(false)
    when(dh.findAuthInfoByAccessToken(at)).thenReturn(Future.value(Some(ai)))

    val authInfo: Endpoint[IO, AuthInfo[Int]] = authorize(dh)
    val e: Endpoint[IO, Int] = get("user" :: authInfo) { ai: AuthInfo[Int] =>
      Ok(ai.user)
    }

    val i1 = Input.get("/user", "access_token" -> "bar")
    val i2 = Input.get("/user")

    e(i1).awaitOutputUnsafe() shouldBe Some(Ok(42))
    val Some(error) = e(i2).awaitOutputUnsafe()
    error.status shouldBe Status.BadRequest
    error.headers should contain key "WWW-Authenticate"
  }

  it should "issue the access token" in {
    val dh: DataHandler[Int] = mock[DataHandler[Int]]
    val at: AccessToken = AccessToken("foobar", None, None, None, null)

    when(dh.validateClient("id", "", "password")).thenReturn(Future.value(true))
    when(dh.findUser("u", "p")).thenReturn(Future.value(Some(42)))
    when(dh.getStoredAccessToken(AuthInfo(42, "id", None, None))).thenReturn(Future.value(Some(at)))
    when(dh.isAccessTokenExpired(at)).thenReturn(false)

    val grandHandlerResult: Endpoint[IO, GrantResult] = issueAccessToken(dh)
    val e: Endpoint[IO, String] = get("token" :: grandHandlerResult) { ghr: GrantResult =>
      Ok(ghr.accessToken)
    }

    val i1 = Input.get("/token",
      "grant_type" -> "password", "username" -> "u", "password" -> "p", "client_id" -> "id"
    )

    val i2 = Input.get("/token")

    e(i1).awaitOutputUnsafe() shouldBe Some(Ok("foobar"))
    val Some(error) = e(i2).awaitOutputUnsafe()
    error.status shouldBe Status.BadRequest
    error.headers should contain key "WWW-Authenticate"
  }
} 
Example 12
Source File: FinagleBackend.scala    From airframe   with Apache License 2.0 5 votes vote down vote up
package wvlet.airframe.http.finagle

import java.util.concurrent.atomic.AtomicReference

import com.twitter.finagle.Service
import com.twitter.finagle.context.Contexts
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.util.{Future, Promise, Return, Throw}
import wvlet.airframe.http.{HttpBackend, HttpRequestAdapter, HttpStatus}
import wvlet.log.LogSupport

import scala.concurrent.ExecutionContext
import scala.util.{Failure, Success}
import scala.{concurrent => sc}


object FinagleBackend extends HttpBackend[Request, Response, Future] {
  override protected implicit val httpRequestAdapter: HttpRequestAdapter[Request] = FinagleHttpRequestAdapter

  override def wrapException(e: Throwable): Future[Response] = {
    Future.exception(e)
  }
  override def newResponse(status: HttpStatus, content: String): Response = {
    val r = Response(Status.fromCode(status.code))
    r.contentString = content
    r
  }

  def wrapFilter(filter: com.twitter.finagle.Filter[Request, Response, Request, Response]): FinagleFilter = {
    new FinagleFilter with LogSupport {
      override def apply(request: Request, context: Context): Future[Response] = {
        filter(request, Service.mk { req: Request => context(req) })
      }
    }
  }

  override def toFuture[A](a: A): Future[A] = Future.value(a)
  override def toScalaFuture[A](a: Future[A]): sc.Future[A] = {
    val promise: sc.Promise[A] = sc.Promise()
    a.respond {
      case Return(value)    => promise.success(value)
      case Throw(exception) => promise.failure(exception)
    }
    promise.future
  }
  override def toFuture[A](a: sc.Future[A], e: ExecutionContext): Future[A] = {
    val promise: Promise[A] = Promise()
    a.onComplete {
      case Success(value)     => promise.setValue(value)
      case Failure(exception) => promise.setException(exception)
    }(e)
    promise
  }

  override def isFutureType(cl: Class[_]): Boolean = {
    classOf[Future[_]].isAssignableFrom(cl)
  }
  override def isRawResponseType(cl: Class[_]): Boolean = {
    classOf[Response].isAssignableFrom(cl)
  }
  override def mapF[A, B](f: Future[A], body: A => B): Future[B] = {
    f.map(body)
  }

  private val contextParamHolderKey = new Contexts.local.Key[AtomicReference[collection.mutable.Map[String, Any]]]

  override def withThreadLocalStore(body: => Future[Response]): Future[Response] = {
    val newParamHolder = collection.mutable.Map.empty[String, Any]
    Contexts.local
      .let(contextParamHolderKey, new AtomicReference[collection.mutable.Map[String, Any]](newParamHolder)) {
        body
      }
  }

  override def setThreadLocal[A](key: String, value: A): Unit = {
    Contexts.local.get(contextParamHolderKey).foreach { ref => ref.get().put(key, value) }
  }

  override def getThreadLocal[A](key: String): Option[A] = {
    Contexts.local.get(contextParamHolderKey).flatMap { ref => ref.get.get(key).asInstanceOf[Option[A]] }
  }
} 
Example 13
Source File: FinagleFilter.scala    From airframe   with Apache License 2.0 5 votes vote down vote up
package wvlet.airframe.http.finagle

import com.twitter.finagle.http.{Request, Response}
import com.twitter.finagle.{Service, SimpleFilter}
import com.twitter.util.Future
import wvlet.airframe.Session
import wvlet.airframe.codec.MessageCodecFactory
import wvlet.airframe.http.{HttpBackend, HttpFilter}
import wvlet.airframe.http.router.HttpRequestDispatcher


abstract class FinagleFilter extends HttpFilter[Request, Response, Future] {
  override def backend: HttpBackend[Request, Response, Future] = FinagleBackend
}

class FinagleRouter(session: Session, private[finagle] val config: FinagleServerConfig)
    extends SimpleFilter[Request, Response] {
  private val dispatcher =
    HttpRequestDispatcher.newDispatcher(
      session,
      config.router,
      config.controllerProvider,
      FinagleBackend,
      config.responseHandler,
      MessageCodecFactory.defaultFactory.orElse(MessageCodecFactory.newFactory(config.customCodec))
    )

  override def apply(request: Request, service: Service[Request, Response]): Future[Response] = {
    dispatcher.apply(request, FinagleBackend.newContext { request: Request => service(request) })
  }
} 
Example 14
Source File: FinagleServerFactoryTest.scala    From airframe   with Apache License 2.0 5 votes vote down vote up
package wvlet.airframe.http.finagle
import com.twitter.finagle.Service
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.finagle.tracing.ConsoleTracer
import com.twitter.util.Future
import wvlet.airframe.control.Control._
import wvlet.airframe.http.Router
import wvlet.airspec.AirSpec


class FinagleServerFactoryTest extends AirSpec {
  def `start multiple FinagleServers`: Unit = {
    val router1 = Router.add[MyApi]
    val router2 = Router.add[MyApi]

    val serverConfig1 = Finagle.server
      .withName("server1")
      .withRouter(router1)
    val serverConfig2 = Finagle.server
      .withName("server2")
      .withRouter(router2)

    finagleDefaultDesign.build[FinagleServerFactory] { factory =>
      val server1 = factory.newFinagleServer(serverConfig1)
      val server2 = factory.newFinagleServer(serverConfig2)

      withResources(
        Finagle.newSyncClient(s"localhost:${server1.port}"),
        Finagle.newSyncClient(s"localhost:${server2.port}")
      ) { (client1, client2) =>
        client1.send(Request("/v1/info")).contentString shouldBe "hello MyApi"
        client2.send(Request("/v1/info")).contentString shouldBe "hello MyApi"
      }
    }
  }

  def `allow customize services`: Unit = {
    val d = Finagle.server.withFallbackService {
      new Service[Request, Response] {
        override def apply(request: Request): Future[Response] = {
          val r = Response(Status.Ok)
          r.contentString = "hello custom server"
          Future.value(r)
        }
      }
    }.design

    d.build[FinagleServer] { server =>
      withResource(Finagle.newSyncClient(s"localhost:${server.port}")) { client =>
        client.send(Request("/v1")).contentString shouldBe "hello custom server"
      }
    }
  }

  def `allow customize Finagle Http Server`: Unit = {
    Finagle.server
      .withTracer(ConsoleTracer)
      .withFallbackService(
        new Service[Request, Response] {
          override def apply(request: Request): Future[Response] = {
            val r = Response(Status.Ok)
            r.contentString = "hello custom server with tracer"
            Future.value(r)
          }
        }
      )
      .start { server =>
        withResource(Finagle.newSyncClient(server.localAddress)) { client =>
          client.send(Request("/v1")).contentString shouldBe "hello custom server with tracer"
        }
      }
  }
} 
Example 15
Source File: LeafFilterTest.scala    From airframe   with Apache License 2.0 5 votes vote down vote up
package wvlet.airframe.http.finagle
import com.twitter.finagle.http.{Request, Response}
import com.twitter.util.Future
import wvlet.airframe.Design
import wvlet.airframe.http.{HttpFilter, Router}
import wvlet.airspec.AirSpec

object LeafFilterTest {
  class MyFilter extends FinagleFilter {
    override def apply(request: Request, context: Context): Future[Response] = {
      val r = Response()
      r.contentString = "leaf filter"
      toFuture(r)
    }
  }
}


class LeafFilterTest extends AirSpec {
  import LeafFilterTest._

  override protected def design: Design = {
    newFinagleServerDesign(name = "leaf-filter-test", router = Router.add[MyFilter])
      .add(finagleSyncClientDesign)
  }

  def `support leaf filters`(client: FinagleSyncClient): Unit = {
    client.get[String]("/") shouldBe "leaf filter"
  }

} 
Example 16
Source File: ThreadLocalStorageTest.scala    From airframe   with Apache License 2.0 5 votes vote down vote up
package wvlet.airframe.http.finagle

import com.twitter.finagle.http.{Request, Response}
import com.twitter.util.Future
import wvlet.airframe.Design
import wvlet.airframe.http.{Endpoint, HttpContext, Router}
import wvlet.airspec.AirSpec


class ThreadLocalStorageTest extends AirSpec {
  class MyApp {
    @Endpoint(path = "/get")
    def get(context: FinagleContext): Unit = {
      context.setThreadLocal("mydata", "hello tls")
    }

    @Endpoint(path = "/read")
    def read(context: FinagleContext): String = {
      context.getThreadLocal[String]("client_id").getOrElse("unknown")
    }
  }

  class TLSReaderFilter extends FinagleFilter {
    override def apply(request: Request, context: HttpContext[Request, Response, Future]): Future[Response] = {
      context.setThreadLocal[String]("client_id", "xxxyyy")

      context(request).map { x =>
        // Read TLS set by the child MyApp service
        val mydata = context.getThreadLocal("mydata")

        if (request.path == "/get") {
          val r = Response()
          r.contentString = mydata.getOrElse("N/A")
          r
        } else {
          x
        }
      }
    }
  }

  override protected def design: Design = {
    val router = Router.add[TLSReaderFilter].andThen[MyApp]
    newFinagleServerDesign(name = "tls-test", router = router)
      .bind[FinagleSyncClient].toProvider { server: FinagleServer =>
        Finagle.client.noRetry.newSyncClient(server.localAddress)
      }
  }

  test("tls test") { client: FinagleSyncClient =>
    test("read thread-local data set at the leaf filter") {
      val resp = client.get[String]("/get")
      resp shouldBe "hello tls"
    }

    test("read thread-local data set by the parent filter") {
      val resp = client.get[String]("/read")
      resp shouldBe "xxxyyy"
    }
  }
} 
Example 17
Source File: CorsFilterTest.scala    From airframe   with Apache License 2.0 5 votes vote down vote up
package wvlet.airframe.http.finagle
import com.twitter.finagle.{Service, SimpleFilter}
import com.twitter.finagle.http.{Request, Response}
import com.twitter.util.Future
import wvlet.airframe.Design
import wvlet.airframe.http.Router
import wvlet.airframe.http.finagle.CorsFilterTest.{CheckFilter, LogFilter, SimpleFilter}
import wvlet.airframe.http.finagle.filter.CorsFilter
import wvlet.airspec.AirSpec
import wvlet.log.LogSupport

object CorsFilterTest {

  // Test SimpleFilter of Finagle
  object CheckFilter extends SimpleFilter[Request, Response] with LogSupport {
    override def apply(request: Request, service: Service[Request, Response]): Future[Response] = {
      info(s"checking request: ${request}")
      service(request)
    }
  }

  object LogFilter extends FinagleFilter with LogSupport {
    override def apply(
        request: Request,
        context: LogFilter.Context
    ): Future[Response] = {
      info(s"logging request: ${request}")
      context(request)
    }
  }

  object SimpleFilter extends FinagleFilter with LogSupport {
    override def apply(
        request: Request,
        context: SimpleFilter.Context
    ): Future[Response] = {
      toFuture {
        info(s"handling request: ${request}")
        val r = Response()
        r.contentString = request.headerMap.mkString(", ")
        r
      }
    }
  }
}


class CorsFilterTest extends AirSpec {

  override protected def design: Design = {
    val r = Router
      .add(LogFilter)
      .andThen(FinagleBackend.wrapFilter(CheckFilter))
      .andThen(CorsFilter.unsafePermissiveFilter)
      .andThen(SimpleFilter)

    debug(r)

    newFinagleServerDesign(router = r)
      .add(finagleSyncClientDesign)
  }

  def `support CORS filter`(client: FinagleSyncClient): Unit = {
    val resp = client.get[String]("/")
    debug(resp)
  }

} 
Example 18
Source File: TemperatureServer.scala    From the-finagle-docs   with MIT License 5 votes vote down vote up
package net.gutefrage.context

import java.util.concurrent.atomic.AtomicLong

import com.twitter.finagle._
import com.twitter.finagle.thrift.Protocols
import com.twitter.logging.{Logger, Logging}
import com.twitter.server.TwitterServer
import com.twitter.util.{Await, Future}
import net.gutefrage.Env
import net.gutefrage.temperature.thrift._
import org.slf4j.bridge.SLF4JBridgeHandler



object TemperatureServer extends TwitterServer with Logging{

  val port = flag[Int]("port", 8080, "port this server should use")
  val env = flag[Env]("env", Env.Local, "environment this server runs")

  override def failfastOnFlagsNotParsed: Boolean = true

  premain {
    SLF4JBridgeHandler.removeHandlersForRootLogger()
    SLF4JBridgeHandler.install()
  }

  onExit {
    info("Shutting down temperature server")
  }

  val appLog = Logger("application")

  def main() {
    appLog.info(s"Starting temperature server in environment ${env().name} on port ${port()}")

    // the actual server implementation
    val service = new TemperatureService.FutureIface {

      val numElements = new AtomicLong()
      val sumTemperature = new AtomicLong()

      override def add(datum: TemperatureDatum): Future[Unit] = {
        numElements.incrementAndGet()
        sumTemperature.addAndGet(datum.celsius)
        Future.Unit
      }

      override def mean(): Future[Double] = {
        // Accessing a given userContext if provided
        UserContext.current.foreach { userContext =>
          appLog.info(s"Call received with context: ${userContext}")
        }

        val n = numElements.get()
        val mean = if(n == 0L) 0.0 else sumTemperature.get() / n
        Future.value(mean)
      }
    }

    // Wrap the service implementation into a real finagle service
    val finagledService: Service[Array[Byte], Array[Byte]] = new TemperatureService.FinagledService(
      service, Protocols.binaryFactory()
    )

    // run and announce the service
    val server = ThriftMux.server
      .withLabel("temperature-service")
      .serveAndAnnounce(
        // schema ! host ! path ! shardId
        name = s"zk!127.0.0.1:2181!/service/${env.name}/temperature!0",
        // bind to local address
        addr = s":${port()}",
        service = finagledService
      )

    // Keep waiting for the server and prevent the java process to exit
    closeOnExit(server)
    Await.ready(server)
  }

} 
Example 19
Source File: TemperatureSensorDtabs.scala    From the-finagle-docs   with MIT License 5 votes vote down vote up
package net.gutefrage.basic

import com.twitter.app.Flaggable
import com.twitter.conversions.time._
import com.twitter.finagle.{Dtab, ThriftMux}
import com.twitter.finagle.util.DefaultTimer
import com.twitter.logging.Logging
import com.twitter.server.TwitterServer
import com.twitter.util.{Await, Future}
import net.gutefrage.temperature.thrift._
import net.gutefrage.{Dtabs, Env}
import org.slf4j.bridge.SLF4JBridgeHandler


  override def failfastOnFlagsNotParsed: Boolean = true

  premain {
    SLF4JBridgeHandler.removeHandlersForRootLogger()
    SLF4JBridgeHandler.install()

    // initialise custom dtabs
    Dtabs.init(baseDtab())
    info(
      s"""|Use base dtab:
          |${Dtab.base.show}
       """.stripMargin)
  }

  onExit {
    info("Shutting down sensor")
  }

  def main(): Unit = {
    // create a thrift client and resolve address via dtabs
    val client = ThriftMux.client.newIface[TemperatureService.FutureIface](
      dest = "/s/temperature",
      label = "temperature-sensor"
    )

    implicit val timer = DefaultTimer.twitter
    val randomTemp = new java.util.Random()

    def sendLoop: Future[Unit] = {
      val datum = TemperatureDatum(randomTemp.nextInt(40) - 10,
                                   System.currentTimeMillis / 1000)
      info(s"Sending data: $datum")
      for {
        _ <- Future.sleep(1.second)
        _ <- client.add(datum)
        _ <- sendLoop
      } yield ()
    }

    Await.ready(sendLoop)
  }

} 
Example 20
Source File: TemperatureServer.scala    From the-finagle-docs   with MIT License 5 votes vote down vote up
package net.gutefrage.basic

import java.util.concurrent.atomic.AtomicLong

import com.twitter.finagle._
import com.twitter.finagle.thrift.Protocols
import com.twitter.logging.Logger
import com.twitter.server.TwitterServer
import com.twitter.util.logging.Logging
import com.twitter.util.{Await, Future}
import net.gutefrage.Env
import net.gutefrage.temperature.thrift._
import org.slf4j.bridge.SLF4JBridgeHandler



object TemperatureServer extends TwitterServer with Logging{

  val port = flag[Int]("port", 8080, "port this server should use")
  val env = flag[Env]("env", Env.Local, "environment this server runs")

  override def failfastOnFlagsNotParsed: Boolean = true

  premain {
    SLF4JBridgeHandler.removeHandlersForRootLogger()
    SLF4JBridgeHandler.install()
  }

  onExit {
    info("Shutting down temperature server")
  }

  val appLog = Logger("application")

  def main() {
    appLog.info(s"Starting temperature server in environment ${env().name} on port ${port()}")

    // the actual server implementation
    val service = new TemperatureService.FutureIface {

      val numElements = new AtomicLong()
      val sumTemperature = new AtomicLong()

      override def add(datum: TemperatureDatum): Future[Unit] = {
        numElements.incrementAndGet()
        sumTemperature.addAndGet(datum.celsius)
        Future.Unit
      }

      override def mean(): Future[Double] = {
        val n = numElements.get()
        val mean = if(n == 0L) 0.0 else sumTemperature.get() / n
        Future.value(mean)
      }
    }

    // Wrap the service implementation into a real finagle service
    val finagledService: Service[Array[Byte], Array[Byte]] = new TemperatureService.FinagledService(
      service, Protocols.binaryFactory()
    )

    // run and announce the service
    val server = ThriftMux.server
      .withLabel("temperature-service")
      .serveAndAnnounce(
        // schema ! host ! path ! shardId
        name = s"zk!127.0.0.1:2181!/service/${env().name}/temperature!0",
        // bind to local address
        addr = s":${port()}",
        service = finagledService
      )

    // Keep waiting for the server and prevent the java process to exit
    closeOnExit(server)
    Await.ready(server)
  }

} 
Example 21
Source File: TemperatureSensorStatic.scala    From the-finagle-docs   with MIT License 5 votes vote down vote up
package net.gutefrage.basic

import com.twitter.conversions.time._
import com.twitter.finagle.ThriftMux
import com.twitter.finagle.util.DefaultTimer
import com.twitter.server.TwitterServer
import com.twitter.util.logging.Logging
import com.twitter.util.{Await, Future}
import net.gutefrage.temperature.thrift._
import net.gutefrage.{Dtabs, Env}
import org.slf4j.bridge.SLF4JBridgeHandler


object TemperatureSensorStatic extends TwitterServer with Logging{
  val env = flag[Env]("env", Env.Local, "environment this server runs")

  premain {
    SLF4JBridgeHandler.removeHandlersForRootLogger()
    SLF4JBridgeHandler.install()

    // initialise custom dtabs
    Dtabs.init()
  }

  onExit {
    info("Shutting down sensor")
  }

  def main(): Unit = {
    // create a thrift client and resolve address via dtabs
    val client = ThriftMux.client.newIface[TemperatureService.FutureIface](
      // schema ! args
      dest = s"zk2!127.0.0.1:2181!/service/${env().name}/temperature",
      label = "temperature-sensor"
    )

    implicit val timer = DefaultTimer.twitter
    val randomTemp = new java.util.Random()

    def sendLoop: Future[Unit] = {
      val datum = TemperatureDatum(randomTemp.nextInt(40) - 10,
                                   System.currentTimeMillis / 1000)
      info(s"Sending data: $datum")
      for {
        _ <- Future.sleep(1.second)
        _ <- client.add(datum)
        _ <- sendLoop
      } yield ()
    }

    Await.ready(sendLoop)
  }

} 
Example 22
Source File: QueryCacheFilter.scala    From the-finagle-docs   with MIT License 5 votes vote down vote up
package net.gutefrage.filter

import com.redis._
import com.redis.serialization.Parse.Implicits._
import com.twitter.finagle.{Service, SimpleFilter}
import com.twitter.util.Future
import net.gutefrage.filter.ThriftByteArray._

class QueryCacheFilter(val methodsToCache: Option[Seq[String]] = None, redisClient: RedisClient) extends SimpleFilter[Array[Byte], Array[Byte]] {

  def bytesToInt(bytes: Array[Byte]): Int = java.nio.ByteBuffer.wrap(bytes).getInt

  def apply(request: Array[Byte], service: Service[Array[Byte], Array[Byte]]): Future[Array[Byte]] = {
    val (methodName, seqId) = request.binaryProtocolMethodNameSeqId

    if (methodsToCache.isEmpty || methodsToCache.get.contains(methodName)) {
      println(s"Incoming request with method: $methodName -> Try to serve it from cache.")
      val redisKey = request.binaryProtocolChangeSeqId(Array[Byte](0, 0, 0, 0)).requestHashKey

      redisClient.get[Array[Byte]](redisKey) match {
        case Some(response) => {
          println("Data in redis found, returning from cache")
          Future.value(response)
        }
        case None => {
          println("data not in cache yet")
          service(request) map { result =>
            redisClient.setex(redisKey, 15, result)
            result
          }
        }
      }

    } else {
      println(s"Incoming request with method: $methodName -> Don't serve it from cache.")
      service(request)
    }

  }

} 
Example 23
Source File: twitter.scala    From interop-twitter   with Apache License 2.0 5 votes vote down vote up
package zio.interop

import com.twitter.util.{ Future, FutureCancelledException, Promise, Return, Throw }
import zio.Cause
import zio.{ Runtime, Task, UIO, ZIO }

package object twitter {
  implicit class TaskObjOps(private val obj: Task.type) extends AnyVal {
    final def fromTwitterFuture[A](future: Task[Future[A]]): Task[A] =
      Task.uninterruptibleMask { restore =>
        future.flatMap { f =>
          restore(Task.effectAsync { cb: (Task[A] => Unit) =>
            val _ = f.respond {
              case Return(a) => cb(Task.succeed(a))
              case Throw(e)  => cb(Task.fail(e))
            }
          }).onInterrupt(UIO(f.raise(new FutureCancelledException)))
        }
      }
  }

  implicit class RuntimeOps[R](private val runtime: Runtime[R]) extends AnyVal {
    def unsafeRunToTwitterFuture[A](io: ZIO[R, Throwable, A]): Future[A] = {
      val promise                                = Promise[A]()
      def failure(cause: Cause[Throwable]): Unit = promise.setException(cause.squash)
      def success(value: A): Unit                = promise.setValue(value)

      runtime.unsafeRunAsync {
        io.fork.flatMap { f =>
          promise.setInterruptHandler {
            case _ => runtime.unsafeRunAsync_(f.interrupt)
          }
          f.join
        }
      }(_.fold(failure, success))

      promise
    }
  }
} 
Example 24
Source File: TwitterSpec.scala    From interop-twitter   with Apache License 2.0 5 votes vote down vote up
package zio.interop

import java.util.concurrent.atomic.AtomicInteger

import com.twitter.util.{ Await, Future, Promise }
import zio.{ Task, ZIO }
import zio.interop.twitter._
import zio.test._
import zio.test.Assertion._

import scala.util.{ Failure, Success, Try }

object TwitterSpec extends DefaultRunnableSpec {
  val runtime = runner.runtime

  override def spec =
    suite("TwitterSpec")(
      suite("Task.fromTwitterFuture")(
        testM("return failing `Task` if future failed.") {
          val error  = new Exception
          val future = Task(Future.exception[Int](error))
          val task   = Task.fromTwitterFuture(future).unit

          assertM(task.either)(isLeft(equalTo(error)))
        },
        testM("return successful `Task` if future succeeded.") {
          val value  = 10
          val future = Task(Future.value(value))
          val task   = Task.fromTwitterFuture(future).option

          assertM(task)(isSome(equalTo(value)))
        },
        testM("ensure future is interrupted together with task.") {
          val value = new AtomicInteger(0)

          val promise = new Promise[Unit] with Promise.InterruptHandler {
            override protected def onInterrupt(t: Throwable): Unit = setException(t)
          }

          val future = Task(promise.flatMap(_ => Future(value.incrementAndGet())))

          val task =
            (for {
              fiber <- Task.fromTwitterFuture(future).fork
              _     <- fiber.interrupt
              _     <- Task.effect(promise.setDone())
              a     <- fiber.await
            } yield a).fold(_ => false, exit => exit.toEither.isLeft)

          task.map(b => assert(b)(isTrue) && assert(value.get())(equalTo(0)))
        }
      ),
      suite("Runtime.unsafeRunToTwitterFuture")(
        test("return successful `Future` if Task evaluation succeeded.") {
          assert(Await.result(runtime.unsafeRunToTwitterFuture(Task.succeed(2))))(equalTo(2))
        },
        test("return failed `Future` if Task evaluation failed.") {
          val e      = new Throwable
          val task   = Task.fail(e).unit
          val result =
            Try(Await.result(runtime.unsafeRunToTwitterFuture(task))) match {
              case Failure(exception) => Some(exception)
              case Success(_)         => None
            }

          assert(result)(isSome(equalTo(e)))
        },
        testM("ensure Task evaluation is interrupted together with Future.") {
          val value                                  = new AtomicInteger(0)
          val ex                                     = new Exception
          val task: ZIO[Any, Throwable, Future[Int]] = for {
            promise <- zio.Promise.make[Throwable, Int]
            t        = promise.await.flatMap(_ => Task.effectTotal(value.incrementAndGet()))
            future   = runtime.unsafeRunToTwitterFuture(t)
            _        = future.raise(ex)
            _       <- promise.succeed(1)
          } yield future

          assertM(task.map(Await.result(_)).run)(isInterrupted).map(_ && assert(value.get)(equalTo(0)))
        }
      )
    )
} 
Example 25
Source File: RoundTripThriftSmallBenchmark.scala    From finagle-serial   with Apache License 2.0 5 votes vote down vote up
package io.github.finagle.serial.benchmark

import java.net.InetSocketAddress
import java.util.concurrent.TimeUnit

import com.twitter.finagle.{Client, Server, Service, ThriftMux}
import com.twitter.util.{Closable, Await, Future}
import org.openjdk.jmh.annotations._


@State(Scope.Thread)
class RoundTripThriftSmallBenchmark {
  private val smallSize = 20

  val small: thriftscala.Small =
    thriftscala.Small((for (i <- 1 to smallSize) yield i % 2 == 0).toList, "foo bar baz")

  val echo = new thriftscala.EchoService.FutureIface {
    def echo(small: thriftscala.Small) = Future.value(small)
  }

  var s: Closable = _
  var c: thriftscala.EchoService.FutureIface = _

  @Setup
  def setUp(): Unit = {
    s = ThriftMux.serveIface(new InetSocketAddress(8124), echo)
    c = ThriftMux.newIface[thriftscala.EchoService.FutureIface]("localhost:8124")
  }

  @TearDown
  def tearDown(): Unit = {
    Await.ready(s.close())
  }

  @Benchmark
  @BenchmarkMode(Array(Mode.Throughput))
  @OutputTimeUnit(TimeUnit.SECONDS)
  def test: thriftscala.Small = Await.result(c.echo(small))
} 
Example 26
Source File: RoundTripBenchmark.scala    From finagle-serial   with Apache License 2.0 5 votes vote down vote up
package io.github.finagle.serial.benchmark

import java.net.InetSocketAddress
import java.util.concurrent.TimeUnit

import com.twitter.finagle.{Server, Client, Service}
import com.twitter.util.{Closable, Await, Future}
import org.openjdk.jmh.annotations._

@State(Scope.Thread)
abstract class RoundTripBenchmark[A](val workload: A) {

  val echo = new Service[A, A] {
    override def apply(a: A) = Future.value(a)
  }

  var s: Closable = _
  var c: Service[A, A] = _

  def server: Server[A, A]
  def client: Client[A, A]

  @Setup
  def setUp(): Unit = {
    s = server.serve(new InetSocketAddress(8123), echo)
    c = client.newService("localhost:8123")
  }

  @TearDown
  def tearDown(): Unit = {
    Await.ready(c.close())
    Await.ready(s.close())
  }

  @Benchmark
  @BenchmarkMode(Array(Mode.Throughput))
  @OutputTimeUnit(TimeUnit.SECONDS)
  def test: A = Await.result(c(workload))
} 
Example 27
Source File: SerialIntegrationTest.scala    From finagle-serial   with Apache License 2.0 5 votes vote down vote up
package io.github.finagle.serial.tests

import com.twitter.finagle.{Client, ListeningServer, Server, Service}
import com.twitter.util.{Await, Future, Try}
import io.github.finagle.serial.Serial
import java.net.{InetAddress, InetSocketAddress}
import org.scalatest.Matchers
import org.scalatest.prop.Checkers
import org.scalacheck.{Arbitrary, Gen, Prop}


  def testFunctionService[I, O](
    f: I => O
  )(implicit
    inCodec: C[I],
    outCodec: C[O],
    arb: Arbitrary[I]
  ): Unit = {
    val (fServer, fClient) = createServerAndClient(f)(inCodec, outCodec)

    check(serviceFunctionProp(fClient)(f)(arb.arbitrary))

    Await.result(fServer.close())
  }
} 
Example 28
Source File: RerunnableBenchmark.scala    From catbird   with Apache License 2.0 5 votes vote down vote up
package io.catbird.benchmark

import com.twitter.util.{ Await, Future, FuturePool }
import io.catbird.util.Rerunnable
import java.util.concurrent.{ ExecutorService, Executors, TimeUnit }
import org.openjdk.jmh.annotations._


@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
class RerunnableBenchmark {
  val count: Int = 100000
  val numbers: IndexedSeq[Int] = 0 to count
  var es: ExecutorService = _
  var pool: FuturePool = _

  @Setup
  def initPool(): Unit = {
    es = Executors.newFixedThreadPool(4)
    pool = FuturePool(es)
  }

  @TearDown
  def shutdownPool(): Unit = es.shutdown()

  @Benchmark
  def sumIntsF: Int = Await.result(
    numbers.foldLeft(Future(0)) {
      case (acc, i) => acc.flatMap(prev => Future(prev + i))
    }
  )

  @Benchmark
  def sumIntsR: Int = Await.result(
    numbers
      .foldLeft(Rerunnable(0)) {
        case (acc, i) => acc.flatMap(prev => Rerunnable(prev + i))
      }
      .run
  )

  @Benchmark
  def sumIntsPF: Int = Await.result(
    numbers.foldLeft(pool(0)) {
      case (acc, i) => acc.flatMap(prev => pool(prev + i))
    }
  )

  @Benchmark
  def sumIntsPR: Int = Await.result(
    numbers
      .foldLeft(Rerunnable.withFuturePool(pool)(0)) {
        case (acc, i) => acc.flatMap(prev => Rerunnable.withFuturePool(pool)(prev + i))
      }
      .run
  )
} 
Example 29
Source File: RerunnableInstances.scala    From catbird   with Apache License 2.0 5 votes vote down vote up
package io.catbird.util.effect

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

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

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

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

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

            promise
          }
        }

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

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

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

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

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

import cats.effect.ContextShift
import com.twitter.util.{ Future, FuturePool, Promise }
import io.catbird.util.Rerunnable

import scala.Unit
import java.lang.Runnable
import java.util.concurrent.ExecutorService

import scala.concurrent.{ ExecutionContext, ExecutionContextExecutorService }


  object Implicits {
    final implicit def global: ContextShift[Rerunnable] = RerunnableContextShift.global
  }
}

final private[effect] class RerunnableContextShift private (ec: ExecutionContext) extends ContextShift[Rerunnable] {
  private final lazy val futurePool = FuturePool.interruptible(ec.asInstanceOf[ExecutionContextExecutorService])

  override def shift: Rerunnable[Unit] =
    Rerunnable.withFuturePool(futurePool)(()) // This is a bit of a hack, but it will have to do

  override def evalOn[A](targetEc: ExecutionContext)(fa: Rerunnable[A]): Rerunnable[A] =
    for {
      r <- executeOn(targetEc)(fa).liftToTry
      _ <- shift
      a <- Rerunnable.fromFuture(Future.value(r).lowerFromTry)
    } yield a

  private def executeOn[A](targetEc: ExecutionContext)(fa: Rerunnable[A]): Rerunnable[A] =
    Rerunnable.fromFuture {
      val p = Promise[A]()

      targetEc.execute(new Runnable {
        override def run(): Unit =
          fa.run.proxyTo[A](p)
      })

      p
    }
} 
Example 31
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 32
Source File: ContextShiftingSuite.scala    From catbird   with Apache License 2.0 5 votes vote down vote up
package io.catbird.util.effect

import cats.effect.{ ContextShift, IO }
import com.twitter.util.{ ExecutorServiceFuturePool, Future, FuturePool }
import org.scalatest.Outcome
import org.scalatest.funsuite.FixtureAnyFunSuite

import scala.concurrent.ExecutionContext

class ContextShiftingSuite extends FixtureAnyFunSuite with ThreadPoolNamingSupport {

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

    val ioPool = newNamedThreadPool(ioPoolName)

    val futurePool: ExecutorServiceFuturePool = // threadpool of Future (often managed by a library like finagle-http)
      FuturePool(newNamedThreadPool(futurePoolName))

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

    def newFuture: Future[String] = futurePool.apply {
      // Not 100% sure why but this sleep is needed to reproduce the error. There might be an optimization if the
      // Future is already resolved at some point
      Thread.sleep(200)
      currentThreadName()
    }
  }

  test("After resolving the Future with futureToAsync stay on the Future threadpool") { f =>
    implicit val contextShift: ContextShift[IO] = // threadpool of IO (often provided by IOApp)
      IO.contextShift(ExecutionContext.fromExecutor(f.ioPool))

    val (futurePoolName, ioPoolName) = (for {
      futurePoolName <- futureToAsync[IO, String](f.newFuture)

      ioPoolName <- f.newIO
    } yield (futurePoolName, ioPoolName)).start(contextShift).flatMap(_.join).unsafeRunSync()

    assert(futurePoolName == f.futurePoolName)
    assert(ioPoolName == f.futurePoolName) // Uh oh, this is likely not what the user wants
  }

  test("After resolving the Future with futureToAsyncAndShift shift back to the threadpool of ContextShift[F]") { f =>
    implicit val contextShift: ContextShift[IO] = // threadpool of IO (often provided by IOApp)
      IO.contextShift(ExecutionContext.fromExecutor(f.ioPool))

    // If you'd use `futureToAsync` here instead, this whole thing would sometimes stay on the future-pool
    val (futurePoolName, ioPoolName) = (for {
      futurePoolName <- futureToAsyncAndShift[IO, String](f.newFuture)

      ioPoolName <- f.newIO
    } yield (futurePoolName, ioPoolName))
      .start(contextShift) // start the computation on the default threadpool...
      .flatMap(_.join) // ...then block until we have the results
      .unsafeRunSync()

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

  override protected def withFixture(test: OneArgTest): Outcome = withFixture(test.toNoArgTest(new FixtureParam))
} 
Example 33
Source File: RerunnableTimerSuite.scala    From catbird   with Apache License 2.0 5 votes vote down vote up
package io.catbird.util.effect

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

import scala.concurrent.duration._

class RerunnableTimerSuite extends FixtureAnyFunSuite {

  protected final class FixtureParam {
    val twitterTimer: util.Timer = new util.JavaTimer()
  }

  test("A timer can be used to delay execution") { f =>
    implicit val timer: Timer[Rerunnable] = RerunnableTimer(f.twitterTimer)

    val result = Await.result(
      Future.selectIndex(
        Vector(
          for {
            _ <- Timer[Rerunnable].sleep(100.milliseconds).run
            r <- Future.value("slow")
          } yield r,
          Future.value("fast").delayed(util.Duration.fromMilliseconds(50))(f.twitterTimer)
        )
      )
    )

    assert(result == 1) // The first future is delayed for longer, so we expect the second one to win
  }

  override protected def withFixture(test: OneArgTest): Outcome = withFixture(test.toNoArgTest(new FixtureParam))
} 
Example 34
Source File: arbitrary.scala    From catbird   with Apache License 2.0 5 votes vote down vote up
package io.catbird.util

import com.twitter.concurrent.AsyncStream
import com.twitter.conversions.DurationOps._
import com.twitter.util.{ Future, Return, Try, Var }
import org.scalacheck.{ Arbitrary, Cogen }

trait ArbitraryInstances {
  implicit def futureArbitrary[A](implicit A: Arbitrary[A]): Arbitrary[Future[A]] =
    Arbitrary(A.arbitrary.map(Future.value))

  implicit def tryArbitrary[A](implicit A: Arbitrary[A]): Arbitrary[Try[A]] =
    Arbitrary(A.arbitrary.map(Return(_)))

  implicit def varArbitrary[A](implicit A: Arbitrary[A]): Arbitrary[Var[A]] =
    Arbitrary(A.arbitrary.map(Var.value))

  implicit def asyncStreamArbitrary[A](implicit A: Arbitrary[A]): Arbitrary[AsyncStream[A]] =
    Arbitrary(A.arbitrary.map(AsyncStream.of))

  implicit def rerunnableArbitrary[A](implicit A: Arbitrary[A]): Arbitrary[Rerunnable[A]] =
    Arbitrary(futureArbitrary[A].arbitrary.map(Rerunnable.fromFuture[A](_)))

  implicit def cogenFuture[A](implicit A: Cogen[A]): Cogen[Future[A]] =
    A.contramap(futureComonad(1.second).extract)

  implicit def cogenVar[A](implicit A: Cogen[A]): Cogen[Var[A]] =
    A.contramap(varComonad.extract)

  implicit def cogenRerunnable[A](implicit A: Cogen[A]): Cogen[Rerunnable[A]] =
    A.contramap(Rerunnable.rerunnableComonad(1.second).extract)
} 
Example 35
Source File: future.scala    From catbird   with Apache License 2.0 5 votes vote down vote up
package io.catbird.util

import cats.instances.either._
import cats.instances.int._
import cats.instances.tuple._
import cats.instances.unit._
import cats.kernel.laws.discipline.{ MonoidTests, SemigroupTests }
import cats.laws.discipline._
import cats.laws.discipline.arbitrary._
import cats.{ Comonad, Eq }
import com.twitter.conversions.DurationOps._
import com.twitter.util.Future
import org.scalacheck.Arbitrary

class FutureSuite extends CatbirdSuite with FutureInstances with ArbitraryInstances with EqInstances {
  implicit val eqFutureInt: Eq[Future[Int]] = futureEqWithFailure(1.second)
  implicit val eqFutureFutureInt: Eq[Future[Future[Int]]] = futureEqWithFailure(1.second)
  implicit val eqFutureFutureFutureInt: Eq[Future[Future[Future[Int]]]] = futureEqWithFailure(1.second)
  implicit val eqFutureInt3: Eq[Future[(Int, Int, Int)]] = futureEqWithFailure(1.second)
  implicit val eqFutureEitherUnit: Eq[Future[Either[Throwable, Unit]]] = futureEqWithFailure(1.second)
  implicit val eqFutureEitherInt: Eq[Future[Either[Throwable, Int]]] = futureEqWithFailure(1.second)
  implicit val comonad: Comonad[Future] = futureComonad(1.second)
  implicit val eqFutureParInt: Eq[FuturePar[Int]] = futureParEqWithFailure(1.second)
  implicit val eqFutureParInt3: Eq[FuturePar[(Int, Int, Int)]] = futureParEqWithFailure(1.second)
  implicit def arbFuturePar[A](implicit A: Arbitrary[A]): Arbitrary[FuturePar[A]] =
    Arbitrary(A.arbitrary.map(value => FuturePar(Future.value(value))))

  checkAll("Future[Int]", MonadErrorTests[Future, Throwable].monadError[Int, Int, Int])
  checkAll("Future[Int]", ComonadTests[Future].comonad[Int, Int, Int])
  checkAll("Future[Int]", FunctorTests[Future](comonad).functor[Int, Int, Int])
  checkAll("Future[Int]", SemigroupTests[Future[Int]](twitterFutureSemigroup[Int]).semigroup)
  checkAll("Future[Int]", MonoidTests[Future[Int]].monoid)
  checkAll("Future[Int]", ParallelTests[Future, FuturePar].parallel[Int, Int])
  checkAll("FuturePar[Int]", CommutativeApplicativeTests[FuturePar].commutativeApplicative[Int, Int, Int])
} 
Example 36
Source File: TodoItemApi.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package examples.todolist
package http

import cats._
import cats.implicits._
import com.twitter.util.Future
import examples.todolist.TodoItem
import examples.todolist.service.TodoItemService
import io.circe.generic.auto._
import io.finch._
import io.finch.circe._

class TodoItemApi[F[_]: Monad](implicit service: TodoItemService[F], handler: F ~> Future)
    extends CRUDApi[TodoItem] {

  import io.finch.syntax._

  private val prefix = "items"

  val reset = post(prefix :: "reset") {
    handler(service.reset.map(Ok))
  }

  val retrieve = get(prefix :: path[Int]) { id: Int =>
    handler(
      service.retrieve(id) map (item =>
        item.fold[Output[TodoItem]](
          NotFound(new NoSuchElementException(s"Could not find ${service.model} with $id")))(Ok)))
  } handle {
    case nse: NoSuchElementException => NotFound(nse)
  }

  val list = get(prefix) {
    handler(service.list.map(Ok))
  }

  val insert = post(prefix :: jsonBody[TodoItem]) { item: TodoItem =>
    handler(service.insert(item).map(Ok))
  }

  val update = put(prefix :: path[Int] :: jsonBody[TodoItem]) { (id: Int, item: TodoItem) =>
    handler(service.update(item.copy(id = Some(id))).map(Ok))
  }

  val destroy = delete(prefix :: path[Int]) { id: Int =>
    handler(service.destroy(id).map(Ok))
  }
}

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

import cats._
import cats.implicits._
import com.twitter.util.Future
import examples.todolist.TodoForm
import examples.todolist.service.AppService
import io.circe.generic.auto._
import io.finch._
import io.finch.circe._

class AppApi[F[_]: Monad](implicit service: AppService[F], handler: F ~> Future) {

  import io.finch.syntax._

  val reset = post("reset") {
    handler(service.reset.map(Ok))
  }

  val list = get("list") {
    handler(service.list.map(Ok))
  }

  val insert = post("insert" :: jsonBody[TodoForm]) { form: TodoForm =>
    handler(service.insert(form).map(Ok))
  } handle {
    case nse: NoSuchElementException => InternalServerError(nse)
  }

  val update = put("update" :: jsonBody[TodoForm]) { form: TodoForm =>
    handler(service.update(form).map(Ok))
  } handle {
    case nse: NoSuchElementException => BadRequest(nse)
  }

  val destroy = delete("delete" :: jsonBody[TodoForm]) { form: TodoForm =>
    handler(service.destroy(form).map(Ok))
  } handle {
    case nse: NoSuchElementException => BadRequest(nse)
  }

  val endpoints = reset :+: list :+: insert :+: update :+: destroy
}

object AppApi {
  implicit def instance[F[_]: Monad](
      implicit service: AppService[F],
      handler: F ~> Future): AppApi[F] =
    new AppApi[F]
} 
Example 38
Source File: TagApi.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package examples.todolist
package http

import cats._
import cats.implicits._
import com.twitter.util.Future
import examples.todolist.Tag
import examples.todolist.service.TagService
import io.circe.generic.auto._
import io.finch._
import io.finch.circe._

class TagApi[F[_]: Monad](implicit service: TagService[F], handler: F ~> Future)
    extends CRUDApi[Tag] {

  import io.finch.syntax._

  private val prefix = "tags"

  val reset = post(prefix :: "reset") {
    handler(service.reset.map(Ok))
  }

  val retrieve = get(prefix :: path[Int]) { id: Int =>
    handler(
      service.retrieve(id) map (item =>
        item.fold[Output[Tag]](
          NotFound(new NoSuchElementException(s"Could not find ${service.model} with $id")))(Ok)))
  } handle {
    case nse: NoSuchElementException => NotFound(nse)
  }

  val list = get(prefix) {
    handler(service.list.map(Ok))
  }

  val insert = post(prefix :: jsonBody[Tag]) { item: Tag =>
    handler(service.insert(item).map(Ok))
  }

  val update = put(prefix :: path[Int] :: jsonBody[Tag]) { (id: Int, item: Tag) =>
    handler(service.update(item.copy(id = Some(id))).map(Ok))
  }

  val destroy = delete(prefix :: path[Int]) { id: Int =>
    handler(service.destroy(id).map(Ok))
  } handle {
    case nse: NoSuchElementException => NotFound(nse)
  }
}

object TagApi {
  implicit def instance[F[_]: Monad](
      implicit service: TagService[F],
      handler: F ~> Future): TagApi[F] =
    new TagApi[F]
} 
Example 39
Source File: TodoListApi.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package examples.todolist
package http

import cats._
import cats.implicits._
import com.twitter.util.Future
import examples.todolist.TodoList
import examples.todolist.service.TodoListService
import io.circe.generic.auto._
import io.finch._
import io.finch.circe._

class TodoListApi[F[_]: Monad](implicit service: TodoListService[F], handler: F ~> Future)
    extends CRUDApi[TodoList] {

  import io.finch.syntax._

  private val prefix = "lists"

  val reset = post(prefix :: "reset") {
    handler(service.reset.map(Ok))
  }

  val retrieve = get(prefix :: path[Int]) { id: Int =>
    handler(
      service.retrieve(id) map (item =>
        item.fold[Output[TodoList]](
          NotFound(new NoSuchElementException(s"Could not find ${service.model} with $id")))(Ok)))
  } handle {
    case nse: NoSuchElementException => NotFound(nse)
  }

  val list = get(prefix) {
    handler(service.list.map(Ok))
  }

  val insert = post(prefix :: jsonBody[TodoList]) { item: TodoList =>
    handler(service.insert(item).map(Ok))
  }

  val update = put(prefix :: path[Int] :: jsonBody[TodoList]) { (id: Int, item: TodoList) =>
    handler(service.update(item.copy(id = Some(id))).map(Ok))
  }

  val destroy = delete(prefix :: path[Int]) { id: Int =>
    handler(service.destroy(id).map(Ok))
  }
}

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

import cats._
import cats.implicits._
import com.twitter.util.Future
import examples.todolist.model.Pong
import freestyle.tagless.logging.LoggingM
import io.finch._

class GenericApi[F[_]: Monad](implicit log: LoggingM[F], handler: F ~> Future) {

  import io.finch.syntax._

  val ping = get("ping") {
    handler(
      for {
        _ <- log.error("Not really an error")
        _ <- log.warn("Not really a warn")
        _ <- log.debug("GET /ping")
      } yield Ok(Pong.current)
    )
  }

  val hello = get("hello") {
    handler(
      for {
        _ <- log.error("Not really an error")
        _ <- log.warn("Not really a warn")
        _ <- log.debug("GET /Hello")
      } yield Ok("Hello World")
    )
  }

  val endpoints = hello :+: ping
}

object GenericApi {
  implicit def instance[F[_]: Monad](
      implicit log: LoggingM[F],
      handler: F ~> Future): GenericApi[F] =
    new GenericApi[F]
} 
Example 41
Source File: TodoListApp.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package examples.todolist

import cats._
import cats.effect.IO
import cats.implicits._
import com.twitter.finagle.http.{Request, Response}
import com.twitter.finagle.{Http, ListeningServer, Service}
import com.twitter.server.TwitterServer
import com.twitter.util.{Await, Future}
import doobie.util.transactor.Transactor
import examples.todolist.http.Api
import examples.todolist.persistence.Persistence
import examples.todolist.services.Services
import freestyle.tagless.config.ConfigM
import freestyle.tagless.config.implicits._
import freestyle.tagless.effects.error.ErrorM
import freestyle.tagless.effects.error.implicits._
import freestyle.tagless.logging.LoggingM
import freestyle.tagless.loggingJVM.log4s.implicits._
import freestyle.tagless.module
import io.circe.generic.auto._
import io.finch.circe._

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

object TodoListApp extends TwitterServer {

  import examples.todolist.runtime.implicits._

  def bootstrap[F[_]: Monad](
      implicit app: App[F],
      handler: F ~> Future,
      T: Transactor[F],
      api: Api[F]): F[ListeningServer] = {

    val service: Service[Request, Response] = api.endpoints.toService
    val log: LoggingM[F]                    = app.services.log
    val cfg: ConfigM[F]                     = app.services.config

    for {
      _      <- log.info("Trying to load application.conf")
      config <- cfg.load
      host = config.string("http.host").getOrElse("localhost")
      port = config.int("http.port").getOrElse("8080")
      _ <- log.debug(s"Host: $host")
      _ <- log.debug(s"Port $port")
    } yield
      Await.ready(
        Http.server.withAdmissionControl
          .concurrencyLimit(maxConcurrentRequests = 10, maxWaiters = 10)
          .serve(s"$host:$port", service)
      )
  }

  def main() =
    bootstrap[IO].unsafeRunAsync {
      case Left(error)   => println(s"Error executing server. ${error.getMessage}")
      case Right(server) => server.close()
    }

} 
Example 42
Source File: finch.scala    From freestyle   with Apache License 2.0 5 votes vote down vote up
package freestyle.free
package http

import cats.Id
import com.twitter.util.Future
import io.catbird.util._
import io.finch.Output
import io.finch.syntax.Mapper
import shapeless.HNil
import shapeless.ops.function.FnToProduct

import freestyle.free._
import freestyle.free.implicits._

object finch extends FinchMapperFrees

private[http] trait FinchMapperFrees extends FinchMapperFreeS1 {

  implicit def mapperFromFreeSOutputHFunction[Op[_], A, B, F, FOB](f: F)(
      implicit ftp: FnToProduct.Aux[F, (A) => FOB],
      ev: FOB <:< FreeS[Op, Output[B]],
      I: FSHandler[Op, Future]): Mapper.Aux[A, B] =
    mapperFromFreeSOutputFunction(a => ev(ftp(f)(a)))

  implicit def mapperFromFreeSOutputHFunctionId[Op[_], A, B, F, FOB](f: F)(
      implicit ftp: FnToProduct.Aux[F, A => FOB],
      ev: FOB <:< FreeS[Op, Output[B]],
      I: FSHandler[Op, Id]): Mapper.Aux[A, B] =
    mapperFromFreeSOutputFunctionId(a => ev(ftp(f)(a)))

  implicit def mapperFromFreeSParOutputHFunction[Op[_], A, B, F, FPOB](f: F)(
      implicit ftp: FnToProduct.Aux[F, (A) => FPOB],
      ev: FPOB <:< FreeS.Par[Op, Output[B]],
      I: FSHandler[Op, Future]): Mapper.Aux[A, B] =
    mapperFromFreeSOutputFunction(a => ev(ftp(f)(a)).freeS)

  implicit def mapperFromFreeSParOutputHFunctionId[Op[_], A, B, F, FPOB](f: F)(
      implicit ftp: FnToProduct.Aux[F, A => FPOB],
      ev: FPOB <:< FreeS.Par[Op, Output[B]],
      I: FSHandler[Op, Id]): Mapper.Aux[A, B] =
    mapperFromFreeSOutputFunctionId(a => ev(ftp(f)(a)).freeS)

  implicit def mapperFromFreeSOutputValue[Op[_], A](fo: FreeS[Op, Output[A]])(
      implicit I: FSHandler[Op, Future]): Mapper.Aux[HNil, A] =
    Mapper.mapperFromFutureOutputValue(fo.interpret[Future])

  implicit def mapperFromFreeSOutputValueId[Op[_], A](fo: FreeS[Op, Output[A]])(
      implicit I: FSHandler[Op, Id]): Mapper.Aux[HNil, A] =
    Mapper.mapperFromOutputValue(fo.interpret[Id])

  implicit def mapperFromFreeSParOutputValue[Op[_], A](fpo: FreeS.Par[Op, Output[A]])(
      implicit I: FSHandler[Op, Future]): Mapper.Aux[HNil, A] =
    mapperFromFreeSOutputValue(fpo.freeS)

  implicit def mapperFromFreeSParOutputValueId[Op[_], A](fpo: FreeS.Par[Op, Output[A]])(
      implicit I: FSHandler[Op, Id]): Mapper.Aux[HNil, A] =
    mapperFromFreeSOutputValueId(fpo.freeS)

}

private[http] trait FinchMapperFreeS1 {

  implicit def mapperFromFreeSOutputFunction[Op[_], A, B](f: A => FreeS[Op, Output[B]])(
      implicit I: FSHandler[Op, Future]): Mapper.Aux[A, B] =
    Mapper.mapperFromFutureOutputFunction(a => f(a).interpret[Future])

  implicit def mapperFromFreeSOutputFunctionId[Op[_], A, B](f: A => FreeS[Op, Output[B]])(
      implicit I: FSHandler[Op, Id]): Mapper.Aux[A, B] =
    Mapper.mapperFromOutputFunction(a => f(a).interpret[Id])

  implicit def mapperFromFreeSParOutputFunction[Op[_], A, B](f: A => FreeS.Par[Op, Output[B]])(
      implicit I: FSHandler[Op, Future]): Mapper.Aux[A, B] =
    mapperFromFreeSOutputFunction(f.andThen(_.freeS))

  implicit def mapperFromFreeSParOutputFunctionId[Op[_], A, B](f: A => FreeS.Par[Op, Output[B]])(
      implicit I: FSHandler[Op, Id]): Mapper.Aux[A, B] =
    mapperFromFreeSOutputFunctionId(f.andThen(_.freeS))

} 
Example 43
Source File: CustomTelemetryService.scala    From finagle-prometheus   with MIT License 5 votes vote down vote up
package com.samstarling.prometheusfinagle.examples

import java.text.SimpleDateFormat
import java.util.Calendar

import com.samstarling.prometheusfinagle.metrics.Telemetry
import com.twitter.finagle.Service
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.util.Future

class CustomTelemetryService(telemetry: Telemetry)
    extends Service[Request, Response] {

  private val dayOfWeekFormat = new SimpleDateFormat("E")

  private val counter = telemetry.counter("requests_by_day_of_week",
                                          "Help text",
                                          Seq("day_of_week"))

  override def apply(request: Request): Future[Response] = {
    dayOfWeek
    counter.labels(dayOfWeek).inc()
    val rep = Response(request.version, Status.Ok)
    rep.setContentString("Your request was logged!")
    Future(rep)
  }

  private def dayOfWeek: String = {
    dayOfWeekFormat.format(Calendar.getInstance.getTime)
  }
} 
Example 44
Source File: EmojiService.scala    From finagle-prometheus   with MIT License 5 votes vote down vote up
package com.samstarling.prometheusfinagle.examples

import com.twitter.finagle.{Http, Service}
import com.twitter.finagle.http.{Method, Request, Response, Status}
import com.twitter.finagle.loadbalancer.LoadBalancerFactory
import com.twitter.finagle.stats.{DefaultStatsReceiver, StatsReceiver}
import com.twitter.util.Future

class EmojiService(statsReceiver: StatsReceiver)
    extends Service[Request, Response] {

  private val client = Http.client
    .withTls("api.github.com")
    .withStatsReceiver(statsReceiver)
    .withHttpStats
    .configured(LoadBalancerFactory.HostStats(statsReceiver.scope("host")))
    .newService("api.github.com:443", "GitHub")

  private val emojiRequest = Request(Method.Get, "/emojis")
  emojiRequest.headerMap.add("User-Agent", "My-Finagle-Example")

  override def apply(request: Request): Future[Response] = {
    client.apply(emojiRequest).map { resp =>
      val r = Response(request.version, Status.Ok)
      r.setContentString(resp.getContentString())
      r
    }
  }
} 
Example 45
Source File: MetricsService.scala    From finagle-prometheus   with MIT License 5 votes vote down vote up
package com.samstarling.prometheusfinagle.metrics

import java.io.StringWriter

import com.twitter.finagle.Service
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.util.Future
import io.prometheus.client.CollectorRegistry
import io.prometheus.client.exporter.common.TextFormat

class MetricsService(registry: CollectorRegistry)
    extends Service[Request, Response] {

  override def apply(request: Request): Future[Response] = {
    val writer = new StringWriter
    TextFormat.write004(writer, registry.metricFamilySamples())
    val response = Response(request.version, Status.Ok)
    response.setContentString(writer.toString)
    Future(response)
  }
} 
Example 46
Source File: HttpMonitoringFilter.scala    From finagle-prometheus   with MIT License 5 votes vote down vote up
package com.samstarling.prometheusfinagle.filter

import com.samstarling.prometheusfinagle.metrics.Telemetry
import com.twitter.finagle.http.{Request, Response}
import com.twitter.finagle.{Service, SimpleFilter}
import com.twitter.util.Future

class HttpMonitoringFilter(telemetry: Telemetry,
                           labeller: ServiceLabeller[Request, Response] =
                             new HttpServiceLabeller)
    extends SimpleFilter[Request, Response] {

  private val counter = telemetry.counter(
    name = "incoming_http_requests_total",
    help = "The number of incoming HTTP requests",
    labelNames = labeller.keys
  )

  override def apply(request: Request,
                     service: Service[Request, Response]): Future[Response] = {
    service(request) onSuccess { response =>
      counter.labels(labeller.labelsFor(request, response): _*).inc()
    }
  }
} 
Example 47
Source File: HttpLatencyMonitoringFilter.scala    From finagle-prometheus   with MIT License 5 votes vote down vote up
package com.samstarling.prometheusfinagle.filter

import com.samstarling.prometheusfinagle.metrics.Telemetry
import com.twitter.finagle.http.{Request, Response}
import com.twitter.finagle.{Service, SimpleFilter}
import com.twitter.util.{Future, Stopwatch}

class HttpLatencyMonitoringFilter(telemetry: Telemetry,
                                  buckets: Seq[Double],
                                  labeller: ServiceLabeller[Request, Response] =
                                    new HttpServiceLabeller)
    extends SimpleFilter[Request, Response] {

  private val histogram = telemetry.histogram(
    name = "incoming_http_request_latency_seconds",
    help = "A histogram of the response latency for HTTP requests",
    labelNames = labeller.keys,
    buckets = buckets
  )

  override def apply(request: Request,
                     service: Service[Request, Response]): Future[Response] = {
    val stopwatch = Stopwatch.start()
    service(request) onSuccess { response =>
      histogram
        .labels(labeller.labelsFor(request, response): _*)
        .observe(stopwatch().inMilliseconds / 1000.0)
    }
  }
} 
Example 48
Source File: HttpLatencyMonitoringFilterSpec.scala    From finagle-prometheus   with MIT License 5 votes vote down vote up
package com.samstarling.prometheusfinagle.filter

import com.samstarling.prometheusfinagle.UnitTest
import com.samstarling.prometheusfinagle.helper.{CollectorHelper, CollectorRegistryHelper}
import com.samstarling.prometheusfinagle.metrics.Telemetry
import com.twitter.finagle.Service
import com.twitter.finagle.http.{Method, Request, Response, Status}
import com.twitter.finagle.util.DefaultTimer
import com.twitter.util.{Await, Duration, Future, Timer}
import io.prometheus.client.CollectorRegistry
import org.specs2.specification.Scope

class HttpLatencyMonitoringFilterSpec extends UnitTest {

  class SlowService extends Service[Request, Response] {
    implicit val timer = DefaultTimer.twitter

    override def apply(request: Request): Future[Response] = {
      Future
        .value(Response(request.version, Status.Ok))
        .delayed(Duration.fromMilliseconds(1500))
    }
  }

  trait Context extends Scope {
    val registry = new CollectorRegistry(true)
    val registryHelper = CollectorRegistryHelper(registry)
    val telemetry = new Telemetry(registry, "test")
    val buckets = Seq(1.0, 2.0)
    val labeller = new TestLabeller
    val filter = new HttpLatencyMonitoringFilter(telemetry, buckets, labeller)
    val service = mock[Service[Request, Response]]
    val slowService = new SlowService
    val request = Request(Method.Get, "/foo/bar")
    val serviceResponse = Response(Status.Created)
    val histogram =
      telemetry.histogram(name = "incoming_http_request_latency_seconds")
    service.apply(request) returns Future.value(serviceResponse)
  }

  "HttpLatencyMonitoringFilter" >> {
    "passes requests on to the next service" in new Context {
      Await.result(filter.apply(request, service))
      there was one(service).apply(request)
    }

    "returns the Response from the next service" in new Context {
      val actualResponse = Await.result(filter.apply(request, service))
      actualResponse ==== serviceResponse
    }

    "counts the request" in new Context {
      Await.result(filter.apply(request, slowService))

      registryHelper.samples
        .get("test_incoming_http_request_latency_seconds_count")
        .map(_.map(_.value).sum) must beSome(1.0).eventually
    }

    "increments the counter with the labels from the labeller" in new Context {
      Await.result(filter.apply(request, service))

      registryHelper.samples
        .get("test_incoming_http_request_latency_seconds_count")
        .map(_.head.dimensions.get("foo").get) must beSome("bar").eventually
    }

    "categorises requests into the correct bucket" in new Context {
      Await.result(filter.apply(request, slowService))

      // Our request takes ~1500ms, so it should NOT fall into the "less than or equal to 1 second" bucket (le=0.5)
      registryHelper.samples
        .get("test_incoming_http_request_latency_seconds_bucket")
        .flatMap(_.find(_.dimensions.get("le").contains("1.0")))
        .map(_.value) must beSome(0.0).eventually

      // However, it should fall into the "less than or equal to 2 seconds" bucket (le=0.5)
      registryHelper.samples
        .get("test_incoming_http_request_latency_seconds_bucket")
        .flatMap(_.find(_.dimensions.get("le").contains("2.0")))
        .map(_.value) must beSome(1.0).eventually

      // It should also fall into the "+Inf" bucket
      registryHelper.samples
        .get("test_incoming_http_request_latency_seconds_bucket")
        .flatMap(_.find(_.dimensions.get("le").contains("+Inf")))
        .map(_.value) must beSome(1.0).eventually
    }
  }
} 
Example 49
Source File: HttpMonitoringFilterSpec.scala    From finagle-prometheus   with MIT License 5 votes vote down vote up
package com.samstarling.prometheusfinagle.filter

import com.samstarling.prometheusfinagle.UnitTest
import com.samstarling.prometheusfinagle.helper.CollectorHelper
import com.samstarling.prometheusfinagle.metrics.Telemetry
import com.twitter.finagle.Service
import com.twitter.finagle.http.{Method, Request, Response, Status}
import com.twitter.util.{Await, Future}
import io.prometheus.client.CollectorRegistry
import org.specs2.specification.Scope

import scala.collection.JavaConverters._

class HttpMonitoringFilterSpec extends UnitTest {

  trait Context extends Scope {
    val registry = new CollectorRegistry(true)
    val telemetry = new Telemetry(registry, "test")
    val labeller = new TestLabeller
    val filter = new HttpMonitoringFilter(telemetry, labeller)
    val service = mock[Service[Request, Response]]
    val request = Request(Method.Get, "/foo/bar")
    val serviceResponse = Response(Status.Created)
    val counter = telemetry.counter(name = "incoming_http_requests_total")
    service.apply(request) returns Future.value(serviceResponse)
  }

  "HttpMonitoringFilter" >> {
    "passes requests on to the next service" in new Context {
      Await.result(filter.apply(request, service))
      there was one(service).apply(request)
    }

    "returns the Response from the next service" in new Context {
      val actualResponse = Await.result(filter.apply(request, service))
      actualResponse ==== serviceResponse
    }

    "increments the incoming_http_requests_total counter" in new Context {
      Await.result(filter.apply(request, service))
      Await.result(filter.apply(request, service))
      CollectorHelper.firstSampleFor(counter).map { sample =>
        sample.value ==== 2.0
      }
    }

    "adds the correct help label" in new Context {
      Await.result(filter.apply(request, service))
      CollectorHelper.firstMetricFor(counter).map { metric =>
        metric.help ==== "The number of incoming HTTP requests"
      }
    }

    "increments the counter with the labels from the labeller" in new Context {
      Await.result(filter.apply(request, service))
      CollectorHelper.firstSampleFor(counter).map { sample =>
        sample.labelNames.asScala(0) ==== "foo"
        sample.labelValues.asScala(0) ==== "bar"
      }
    }
  }
} 
Example 50
Source File: AddMessage.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package examples.circe

import com.twitter.finagle.Service
import com.twitter.finagle.http.Method.Post
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.util.Future
import io.circe.generic.auto._
import io.fintrospect.RouteSpec
import io.fintrospect.formats.Circe
import io.fintrospect.formats.Circe.Auto._
import io.fintrospect.formats.Circe.responseSpec
import io.fintrospect.parameters.{Body, Path}


class AddMessage(emails: Emails) {
  private val exampleEmail = Email(EmailAddress("[email protected]"), EmailAddress("[email protected]"), "when are you going to be home for dinner", 250)

  private val email = Body.of(Circe.bodySpec[Email](), "email", exampleEmail)

  private def addEmail(address: EmailAddress): Service[Request, Response] =
    InOut(Service.mk {
      newEmail: Email => {
        // validate that the receiver is as passed as the one in the URL
        if (address == newEmail.to) emails.add(newEmail)
        Future(emails.forUser(newEmail.to))
      }
    })

  val route = RouteSpec("add an email and return the new inbox contents for the receiver")
    .body(email)
    .returning(responseSpec(Status.Ok -> "new list of emails for the 'to' user", Seq(exampleEmail)))
    .at(Post) / "email" / Path.of(EmailAddress.spec, "email") bindTo addEmail
} 
Example 51
Source File: FindUserWithEmail.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package examples.circe

import com.twitter.finagle.Service
import com.twitter.finagle.http.Method.Get
import com.twitter.finagle.http.Request
import com.twitter.finagle.http.Status.{NotFound, Ok}
import com.twitter.util.Future
import io.circe.generic.auto._
import io.fintrospect.RouteSpec
import io.fintrospect.formats.Circe.Auto._
import io.fintrospect.parameters.Path


class FindUserWithEmail(emails: Emails) {

  private def findByEmail(email: EmailAddress) = {
    val lookupUserByEmail: Service[Request, Option[EmailAddress]] =
      Service.mk { _: Request => Future(emails.users().find(_.address == email.address)) }

    OptionalOut(lookupUserByEmail)
  }

  val route = RouteSpec("Get the user for the particular email address")
    .returning(Ok -> "found the user")
    .returning(NotFound -> "who is that?")
    .at(Get) / "user" / Path.of(EmailAddress.spec, "address", "user email") bindTo findByEmail
} 
Example 52
Source File: MultiPart_Web_Form_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.forms


// fintrospect-core
object MultiPart_Web_Form_Example extends App {

  import com.twitter.finagle.http.Method.Post
  import com.twitter.finagle.http.path.Root
  import com.twitter.finagle.http.{Request, Response}
  import com.twitter.finagle.{Http, Service}
  import com.twitter.util.Await.ready
  import com.twitter.util.Future
  import io.fintrospect.formats.PlainText.ResponseBuilder._
  import io.fintrospect.parameters.{Body, Form, FormField, MultiPartFile}
  import io.fintrospect.{Module, RouteModule, RouteSpec, ServerRoute}



  val usernameField = FormField.required.string("user")
  val fileField = FormField.required.file("data")
  val form: Body[Form] = Body.multiPartWebForm(usernameField -> "everyone has a name!", fileField -> "file is required!")

  val svc: Service[Request, Response] = Service.mk[Request, Response] {
    req => {
      val postedForm: Form = form <-- req
      if (postedForm.isValid) successMessage(postedForm) else failureMessage(postedForm)
    }
  }

  def failureMessage(postedForm: Form): Future[Response] = {
    val errorString = postedForm.errors.map(e => e.param.name + ": " + e.reason).mkString("\n")
    BadRequest("errors were: " + errorString)
  }

  def successMessage(postedForm: Form): Future[Response] = {
    val name: String = usernameField <-- postedForm
    val data: MultiPartFile = fileField <-- postedForm
    Ok(s"$name posted ${data.filename} which is ${data.length}" + " bytes")
  }

  val route: ServerRoute[Request, Response] = RouteSpec()
    .body(form)
    .at(Post) bindTo svc

  val module: Module = RouteModule(Root).withRoute(route)

  ready(Http.serve(":9999", module.toService))
} 
Example 53
Source File: Web_Form_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.forms

// fintrospect-core
object Web_Form_Example extends App {

  import com.twitter.finagle.http.Method.Post
  import com.twitter.finagle.http.path.Root
  import com.twitter.finagle.http.{Request, Response}
  import com.twitter.finagle.{Http, Service}
  import com.twitter.util.Await.ready
  import com.twitter.util.Future
  import io.fintrospect.formats.PlainText.ResponseBuilder._
  import io.fintrospect.parameters.{Body, Form, FormField}
  import io.fintrospect.{Module, RouteModule, RouteSpec, ServerRoute}

  val nameField = FormField.required.string("name")
  val ageField = FormField.optional.int("age")
  val form: Body[Form] = Body.webForm(nameField -> "everyone has a name!", ageField -> "age is an int!")

  val svc: Service[Request, Response] = Service.mk[Request, Response] {
    req => {
      val postedForm: Form = form <-- req
      if (postedForm.isValid) successMessage(postedForm) else failureMessage(postedForm)
    }
  }

  def failureMessage(postedForm: Form): Future[Response] = {
    val errorString = postedForm.errors.map(e => e.param.name + ": " + e.reason).mkString("\n")
    BadRequest("errors were: " + errorString)
  }

  def successMessage(postedForm: Form): Future[Response] = {
    val name: String = nameField <-- postedForm
    val age: Option[Int] = ageField <-- postedForm
    Ok(s"$name is ${age.map(_.toString).getOrElse("too old to admit it")}")
  }

  val route: ServerRoute[Request, Response] = RouteSpec()
    .body(form)
    .at(Post) bindTo svc

  val module: Module = RouteModule(Root).withRoute(route)

  ready(Http.serve(":9999", module.toService))
}

//curl -v -H"Content-Type: application/x-www-form-urlencoded" -XPOST http://localhost:9999/ --data '&age=asd'
//curl -v -H"Content-Type: application/x-www-form-urlencoded" -XPOST http://localhost:9999/ --data 'name=david&age=12' 
Example 54
Source File: MsgPack_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.msgpack

object MsgPack_Example extends App {

  import com.twitter.finagle.http.Method.Post
  import com.twitter.finagle.http.Request
  import com.twitter.finagle.http.path.Root
  import com.twitter.finagle.{Http, Service}
  import com.twitter.util.Await.result
  import com.twitter.util.Future
  import io.fintrospect.formats.MsgPack.Auto._
  import io.fintrospect.formats.MsgPack.Format.{decode, encode}
  import io.fintrospect.{RouteModule, RouteSpec}

  case class StreetAddress(address: String)

  case class Letter(to: StreetAddress, from: StreetAddress, message: String)

  val replyToLetter = RouteSpec()
    .at(Post) / "reply" bindTo InOut[StreetAddress, Letter](
    Service.mk { in: StreetAddress =>
      Future(Letter(StreetAddress("2 Bob St"), in, "hi fools!"))
    })

  val module = RouteModule(Root).withRoute(replyToLetter)

  Http.serve(":8181", module.toService)


  val request = Request(Post, "reply")
  request.content = encode(StreetAddress("1 hello street"))
  val response = result(Http.newService("localhost:8181")(request))
  println("Response was:" + decode[Letter](response.content))

} 
Example 55
Source File: Patching_Endpoint_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.json_libraries


case class Employee(name: String, age: Option[Int])

// fintrospect-core
// fintrospect-circe
object Patching_Endpoint_Example extends App {

  import com.twitter.finagle.http.Method.Post
  import com.twitter.finagle.http.path.Root
  import com.twitter.finagle.http.{Request, Response}
  import com.twitter.finagle.{Http, Service}
  import com.twitter.util.Await.ready
  import com.twitter.util.Future
  import io.circe.generic.auto._
  import io.fintrospect.formats.Circe
  import io.fintrospect.formats.Circe.JsonFormat._
  import io.fintrospect.formats.Circe.ResponseBuilder._
  import io.fintrospect.parameters.Path
  import io.fintrospect.{Module, RouteModule, RouteSpec, ServerRoute}

  import scala.collection.mutable

  val employees = mutable.Map[Int, Employee](1 -> Employee("David", None))

  val patchBody = Circe.patchBody[Employee]()

  def updateAge(id: Int): Service[Request, Response] =
    Service.mk[Request, Response] {
      req =>
        val patcher = patchBody <-- req
        Future(employees.get(id) match {
          case Some(employee) =>
            employees(id) = patcher(employee)
            Ok(encode(employees.get(id)))
          case _ => NotFound(s"with id $id")
        })
    }

  val route: ServerRoute[Request, Response] = RouteSpec()
    .body(patchBody)
    .at(Post) / Path.int("id") bindTo updateAge

  val module: Module = RouteModule(Root).withRoute(route)

  ready(Http.serve(":9999", module.toService))
}

//curl -v -XPOST http://localhost:9999/1 --data '{"age": 50}' 
Example 56
Source File: Full_Auto_Service_Wrappers_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.json_libraries


case class Profile(name: String, age: Option[Int])

// fintrospect-core
// fintrospect-circe
object Full_Auto_Marshalling_Example extends App {

  import com.twitter.finagle.http.Method.Post
  import com.twitter.finagle.http.path.Root
  import com.twitter.finagle.http.{Request, Response}
  import com.twitter.finagle.{Http, Service}
  import com.twitter.util.Await.ready
  import com.twitter.util.Future
  import io.circe.generic.auto._
  import io.fintrospect.formats.Circe
  import io.fintrospect.formats.Circe.Auto._
  import io.fintrospect.parameters.Body
  import io.fintrospect.{Module, RouteModule, RouteSpec, ServerRoute}

  val insultMe: Service[Profile, Profile] = Service.mk[Profile, Profile] {
    inProfile => Future(inProfile.copy(name = inProfile.name + " Smells"))
  }

  val route: ServerRoute[Request, Response] = RouteSpec()
    .body(Body.of(Circe.bodySpec[Profile]()))
    .at(Post) bindTo InOut(insultMe)

  val module: Module = RouteModule(Root).withRoute(route)

  ready(Http.serve(":9999", module.toService))
}

//curl -v -XPOST http://localhost:9999/ --data '{"name":"David", "age": 50}' 
Example 57
Source File: Swagger_Auto_Docs_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.core

// fintrospect-core
object Swagger_Auto_Docs_Example extends App {

  import argo.jdom.JsonNode
  import com.twitter.finagle.http.Method.Post
  import com.twitter.finagle.http.filter.Cors
  import com.twitter.finagle.http.filter.Cors.HttpFilter
  import com.twitter.finagle.http.path.Root
  import com.twitter.finagle.http.{Request, Response, Status}
  import com.twitter.finagle.{Http, Service}
  import com.twitter.util.Await.ready
  import com.twitter.util.Future
  import io.fintrospect.ContentTypes.APPLICATION_JSON
  import io.fintrospect.formats.Argo.JsonFormat._
  import io.fintrospect.formats.Argo.ResponseBuilder._
  import io.fintrospect.parameters.{Body, Header, Path, Query}
  import io.fintrospect.renderers.ModuleRenderer
  import io.fintrospect.renderers.swagger2dot0.{ApiInfo, Swagger2dot0Json}
  import io.fintrospect.{ApiKey, Module, RouteModule, RouteSpec, ServerRoute}

  def buildResponse(id: Int, sent: JsonNode) = obj("id" -> number(id), "sent" -> sent)

  val exampleBody: JsonNode = array(obj("lastName" -> string("Jane")), obj("name" -> string("Jim")))
  val exampleResponse: JsonNode = buildResponse(222, exampleBody)
  val sentDocument: Body[JsonNode] = Body.json("family description", exampleBody)

  def svc(id: Int): Service[Request, Response] = Service.mk[Request, Response] { req =>
    Ok(buildResponse(id, sentDocument <-- req))
  }

  val securityFilter: Service[String, Boolean] = Service.mk[String, Boolean] { r => Future(r == "secret") }

  val route: ServerRoute[Request, Response] = RouteSpec("a short summary", "a longer description")
    .taking(Query.required.string("firstName", "this is your firstname"))
    .taking(Header.optional.localDate("birthdate", "format yyyy-mm-dd"))
    .producing(APPLICATION_JSON)
    .consuming(APPLICATION_JSON)
    .returning(Status.Ok -> "Valid request accepted", exampleResponse)
    .body(sentDocument)
    .at(Post) / Path.int("id", "custom identifier for this request") bindTo svc

  val docsRenderer: ModuleRenderer = Swagger2dot0Json(
    ApiInfo("My App", "1.0", "This is an extended description of the API's functions")
  )

  val module: Module = RouteModule(Root, docsRenderer)
    .withDescriptionPath(moduleRoot => moduleRoot / "swagger.json")
    .securedBy(ApiKey(Query.required.string("token"), securityFilter))
    .withRoute(route)

  ready(Http.serve(":9999", new HttpFilter(Cors.UnsafePermissivePolicy).andThen(module.toService)))
}

// curl -v http://localhost:9999/swagger.json 
Example 58
Source File: PlayTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.formats

import com.twitter.finagle.Service
import com.twitter.finagle.http.Request
import com.twitter.io.{Buf, Bufs}
import com.twitter.util.Future
import io.fintrospect.formats.JsonFormat.InvalidJsonForDecoding
import io.fintrospect.formats.Play.Auto._
import io.fintrospect.formats.Play.JsonFormat._
import io.fintrospect.formats.Play._
import io.fintrospect.parameters.{Body, BodySpec, Query}
import play.api.libs.json._



object helpers {
  implicit val SAWrites = new Writes[StreetAddress] {
    override def writes(in: StreetAddress) = JsObject(Seq("address" -> JsString(in.address)))
  }
  implicit val SAReads = new Reads[StreetAddress] {
    override def reads(in: JsValue) = JsSuccess(StreetAddress(
      (in \ "address").as[String]
    ))
  }

  implicit val Writes = Json.writes[Letter]
  implicit val Reads = Json.reads[Letter]
}


class PlayAutoTest extends AutoSpec(Play.Auto) {

  import helpers._

  describe("API") {
    it("can find implicits") {
      Play.Auto.InOut[Letter, Letter](Service.mk { in: Letter => Future(in) })
    }
  }

  override def toBuf(l: Letter) = Bufs.utf8Buf(compact(Play.JsonFormat.encode(l)(Writes)))

  override def fromBuf(s: Buf): Letter = decode[Letter](parse(Bufs.asUtf8String(s)))(Reads)

  override def bodySpec: BodySpec[Letter] = Play.bodySpec[Letter]()(helpers.Reads, helpers.Writes)

  override def transform() = Play.Auto.tToJsValue[Letter](Writes)
}

class PlayJsonResponseBuilderTest extends JsonResponseBuilderSpec(Play)

class PlayJsonFormatTest extends JsonFormatSpec(Play) {

  describe("Play.JsonFormat") {
    val aLetter = Letter(StreetAddress("my house"), StreetAddress("your house"), "hi there")

    it("roundtrips to JSON and back") {
      val encoded = Play.JsonFormat.encode(aLetter)(helpers.Writes)
      Play.JsonFormat.decode[Letter](encoded)(helpers.Reads) shouldBe aLetter
    }

    it("invalid extracted JSON throws up") {
      intercept[InvalidJsonForDecoding](Play.JsonFormat.decode[Letter](Play.JsonFormat.obj())(helpers.Reads))
    }

    it("body spec decodes content") {
      (Body.of(bodySpec[Letter]()(helpers.Reads, helpers.Writes)) <-- Play.ResponseBuilder.Ok(encode(aLetter)(helpers.Writes)).build()) shouldBe aLetter
    }

    it("param spec decodes content") {
      val param = Query.required(parameterSpec[Letter]()(helpers.Reads, helpers.Writes), "name")
      (param <-- Request("?name=" + encode(aLetter)(helpers.Writes))) shouldBe aLetter
    }
  }

} 
Example 59
Source File: CirceTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.formats

import com.twitter.finagle.Service
import com.twitter.finagle.http.{Request, Status}
import com.twitter.io.{Buf, Bufs}
import com.twitter.util.Future
import io.circe.generic.auto._
import io.fintrospect.formats.Circe.Auto._
import io.fintrospect.formats.Circe.JsonFormat._
import io.fintrospect.formats.Circe._
import io.fintrospect.formats.JsonFormat.InvalidJsonForDecoding
import io.fintrospect.parameters.{Body, BodySpec, Query}



class CirceJsonResponseBuilderTest extends JsonResponseBuilderSpec(Circe)

class CirceAutoTest extends AutoSpec(Circe.Auto) {

  describe("API") {
    it("can find implicits") {
      Circe.Auto.InOut[Letter, Letter](Service.mk { in: Letter => Future(in) })
    }
  }


  override def toBuf(l: Letter) = Bufs.utf8Buf(encode(l).noSpaces)

  override def fromBuf(s: Buf): Letter = decode[Letter](parse(Bufs.asUtf8String(s)))

  override def bodySpec: BodySpec[Letter] = Circe.bodySpec[Letter]()

  override def transform() = Circe.Auto.tToJson[Letter]
}

class CirceJsonFormatTest extends JsonFormatSpec(Circe) {

  import io.circe.generic.auto._

  describe("Circe.JsonFormat") {
    val aLetter = Letter(StreetAddress("my house"), StreetAddress("your house"), "hi there")

    it("roundtrips to JSON and back") {
      val encoded = encode(aLetter)
      decode[Letter](encoded) shouldBe aLetter
    }

    it("patchbody modifies original object with a non-null value") {
      val original = LetterOpt(StreetAddress("my house"), StreetAddress("your house"), None)
      val modifier = encode(obj("message" -> string("hi there")))
      val modifyLetter = patcher[LetterOpt](modifier)
      modifyLetter(original) shouldBe LetterOpt(StreetAddress("my house"), StreetAddress("your house"), Option("hi there"))
    }

    // wait for circe 0.6.X, where this bug will be fixed - https://github.com/travisbrown/circe/issues/304
    ignore("patcher modifies original object with a null value") {
      val original = LetterOpt(StreetAddress("my house"), StreetAddress("your house"), Option("hi there"))
      val modifier = encode(obj())
      val modifyLetter = patcher[LetterOpt](modifier)
      modifyLetter(original) shouldBe LetterOpt(StreetAddress("my house"), StreetAddress("your house"), None)
    }

    it("invalid extracted JSON throws up") {
      intercept[InvalidJsonForDecoding](decode[Letter](Circe.JsonFormat.obj()))
    }

    it("body spec decodes content") {
      (Body.of(bodySpec[Letter]()) <-- Circe.ResponseBuilder.Ok(encode(aLetter)).build()) shouldBe aLetter
    }

    it("patch body can be used to modify an existing case class object") {
      val letterWithNoMessage = LetterOpt(StreetAddress("my house"), StreetAddress("your house"), None)
      val modifiedMessage = encode(obj("message" -> string("hi there")))
      val modifiedLetterWithMessage = LetterOpt(StreetAddress("my house"), StreetAddress("your house"), Some("hi there"))

      val patch = patchBody[LetterOpt]("path to body", modifiedLetterWithMessage) <-- Circe.ResponseBuilder.Ok(modifiedMessage).build()

      patch(letterWithNoMessage) shouldBe modifiedLetterWithMessage
    }

    it("response spec has correct code") {
      Circe.responseSpec[Letter](Status.Ok -> "ok", aLetter).status shouldBe Status.Ok
    }

    it("param spec decodes content") {
      val param = Query.required(parameterSpec[Letter](), "name")
      (param <-- Request("?name=" + encode(aLetter))) shouldBe aLetter
    }
  }
  override val expectedJson: String = """{"string":"hello","object":{"field1":"aString"},"int":10,"long":2,"double":1.2,"decimal":1.2,"bigInt":12344,"bool":true,"null":null,"array":["world",true]}"""
} 
Example 60
Source File: RouteClient.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect

import com.twitter.finagle.http.{Method, Request}
import com.twitter.finagle.{Filter, Service}
import com.twitter.util.Future
import com.twitter.util.Future.exception
import io.fintrospect.parameters.{Binding, PathBinding, PathParameter}

object RouteClient {
  private def identify[O](method: Method, pathParams: Seq[PathParameter[_]]) = Filter.mk[Request, O, Request, O] {
    (request, svc) => {
      request.headerMap(Headers.IDENTIFY_SVC_HEADER) = method + ":" + pathParams.map(_.toString()).mkString("/")
      svc(request)
    }
  }
}


  def apply(userBindings: Iterable[Binding]*): Future[Rsp] = {
    val suppliedBindings = userBindings.flatten ++ providedBindings

    val userSuppliedParams = suppliedBindings.map(_.parameter).filter(_ != null)

    val missing = requiredParams.diff(userSuppliedParams)
    val unknown = userSuppliedParams.diff(allPossibleParams)

    if (missing.nonEmpty) exception(new BrokenContract(s"Client: Missing required params passed: [${missing.mkString(", ")}]"))
    else if (unknown.nonEmpty) exception(new BrokenContract(s"Client: Unknown params passed: [${unknown.mkString(", ")}]"))
    else service(buildRequest(suppliedBindings))
  }

  private def buildRequest(suppliedBindings: Seq[Binding]): Request = suppliedBindings
    .sortBy(p => pathParams.indexOf(p.parameter))
    .foldLeft(RequestBuilder(method)) { (requestBuild, next) => next(requestBuild) }.build()
} 
Example 61
Source File: MultiBodyType.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.util

import com.twitter.finagle.Service
import com.twitter.finagle.http.{Request, Response}
import com.twitter.util.Future
import io.fintrospect.ContentType
import io.fintrospect.formats.Argo.ResponseBuilder._
import io.fintrospect.parameters.Body
import io.fintrospect.renderers.ModuleRenderer
import io.fintrospect.renderers.simplejson.SimpleJson


object MultiBodyType {
  type SupportedContentType = (Body[_], Service[Request, Response])

  def apply(services: SupportedContentType*)(implicit moduleRenderer: ModuleRenderer = SimpleJson()): Service[Request, Response] = {
    val supportedContentTypes = Map(services.map(bs => ContentType(bs._1.contentType.value.toLowerCase) -> bs): _*)

    def validateAndRespond(request: Request, body: SupportedContentType) = body._1.extract(request) match {
      case ExtractionFailed(invalid) => Future(moduleRenderer.badRequest(invalid))
      case _ => body._2(request)
    }

    def handle(request: Request, contentType: ContentType): Future[Response] =
      supportedContentTypes.get(contentType)
        .map(pair => validateAndRespond(request, pair))
        .getOrElse(UnsupportedMediaType(contentType.value))

    Service.mk {
      request: Request =>
        (ContentType.header <-- request)
          .map(value => ContentType(value.toLowerCase()))
          .map(contentType => handle(request, contentType))
          .getOrElse(UnsupportedMediaType("missing Content-Type header"))
    }
  }

} 
Example 62
Source File: HeapDump.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.util

import java.io.File
import java.lang.management.ManagementFactory.getPlatformMXBeans
import java.time.Clock
import java.time.ZonedDateTime.now
import java.time.format.DateTimeFormatter.ISO_DATE_TIME

import com.sun.management.HotSpotDiagnosticMXBean
import com.twitter.finagle.Service
import com.twitter.finagle.http.{Request, Response}
import com.twitter.io.Readers
import com.twitter.util.Future
import io.fintrospect.ContentType
import io.fintrospect.formats.ResponseBuilder.HttpResponse


class HeapDump(processIdentifier: String = "", clock: Clock = Clock.systemUTC()) extends Service[Request, Response] {
  override def apply(request: Request): Future[Response] = {
    val dumpFileName = s"heapdump-$processIdentifier-${now(clock).format(ISO_DATE_TIME)}"
    val dumpFile = File.createTempFile(dumpFileName, ".hprof")
    dumpFile.delete()

    getPlatformMXBeans(classOf[HotSpotDiagnosticMXBean]).get(0).dumpHeap(dumpFile.getAbsolutePath, true)

    val response = HttpResponse(ContentType("application/x-heap-dump"))
      .withHeaders("Content-disposition" -> ("inline; filename=\"" + dumpFileName + ".hprof\""))
      .withContent(Readers.newFileReader(dumpFile, 1024)).build()

    Future(response).ensure(dumpFile.delete())
  }
} 
Example 63
Source File: RequestFilters.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.filters

import java.nio.charset.StandardCharsets.ISO_8859_1
import java.util.Base64

import com.twitter.finagle.Filter
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.util.Future
import io.fintrospect.ContentType.fromAcceptHeaders
import io.fintrospect.configuration.{Authority, Credentials}
import io.fintrospect.formats.Argo.ResponseBuilder._
import io.fintrospect.renderers.ModuleRenderer
import io.fintrospect.renderers.simplejson.SimpleJson
import io.fintrospect.util.{Extracted, Extraction, ExtractionFailed, Extractor}
import io.fintrospect.{ContentType, ContentTypes}
import io.netty.handler.codec.http.HttpHeaderNames


  def ExtractBody[I](extractor: Extractor[Request, I])
                    (implicit moduleRenderer: ModuleRenderer = SimpleJson()):
  Filter[Request, Response, I, Response] = Filter.mk[Request, Response, I, Response] {
    (req, svc) => {
      extractor <--? req match {
        case Extracted(x) => svc(x)
        case ExtractionFailed(invalid) => Future(moduleRenderer.badRequest(invalid))
      }
    }
  }
}


object A extends App {
  println(HttpHeaderNames.USER_AGENT.toString)
} 
Example 64
Source File: SecurityTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect

import com.twitter.finagle.Service
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.util.Await.result
import com.twitter.util.Future
import io.fintrospect.formats.PlainText.ResponseBuilder._
import io.fintrospect.parameters.Query
import io.fintrospect.util.HttpRequestResponseUtil.statusAndContentFrom
import org.scalatest.{FunSpec, Matchers}

class SecurityTest extends FunSpec with Matchers {

  describe("ApiKey") {
    val paramName = "name"
    val param = Query.required.int(paramName)
    val next = Service.mk[Request, Response](r => Ok("hello"))

    it("valid API key is granted access and result carried through") {
      val (status, content) =
        result(ApiKey(param, Service.const(Future(true))).filter(Request(paramName -> "1"), next)
          .map(statusAndContentFrom))

      status should be(Status.Ok)
      content should be("hello")
    }

    it("missing API key is unauthorized") {
      val (status, content) =
        result(ApiKey(param, Service.const(Future(true))).filter(Request(), next)
          .map(statusAndContentFrom))

      status should be(Status.Unauthorized)
      content should be("")
    }

    it("bad API key is unauthorized") {
      val (status, content) =
        result(ApiKey(param, Service.const(Future(true))).filter(Request(paramName -> "notAnInt"), next)
          .map(statusAndContentFrom))

      status should be(Status.Unauthorized)
      content should be("")
    }

    it("unknown API key is unauthorized") {
      val (status, content) =
        result(ApiKey(param, Service.const(Future(false))).filter(Request(paramName -> "1"), next)
          .map(statusAndContentFrom))

      status should be(Status.Unauthorized)
      content should be("")
    }

    it("failed API key lookup is rethrown") {
      val e = new RuntimeException("boom")
      val caught = intercept[RuntimeException](result(ApiKey(param, Service.const(Future.exception(e))).filter(Request(paramName -> "1"), next)))
      caught should be(e)
    }
  }
} 
Example 65
Source File: ArgoJsonModuleRendererTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.renderers

import com.twitter.finagle.Service
import com.twitter.finagle.http.Method.{Get, Post}
import com.twitter.finagle.http.path.Root
import com.twitter.finagle.http.{Request, Status}
import com.twitter.util.{Await, Future}
import io.fintrospect.ContentTypes.{APPLICATION_ATOM_XML, APPLICATION_JSON, APPLICATION_SVG_XML}
import io.fintrospect._
import io.fintrospect.formats.Argo
import io.fintrospect.formats.Argo.JsonFormat.{number, obj, parse}
import io.fintrospect.parameters._
import io.fintrospect.util.HttpRequestResponseUtil.statusAndContentFrom
import io.fintrospect.util.{Echo, ExtractionError}
import org.scalatest.{FunSpec, Matchers}

import scala.io.Source

abstract class ArgoJsonModuleRendererTest() extends FunSpec with Matchers {
  def name: String = this.getClass.getSimpleName

  def renderer: ModuleRenderer

  describe(name) {
    it("renders as expected") {

      val customBody = Body.json("the body of the message", obj("anObject" -> obj("notAStringField" -> number(123))))

      val module = RouteModule(Root / "basepath", renderer)
        .securedBy(ApiKey(Header.required.string("the_api_key"), Service.const(Future(true))))
        .withRoute(
          RouteSpec("summary of this route", "some rambling description of what this thing actually does")
            .producing(APPLICATION_JSON)
            .taking(Header.optional.string("header", "description of the header"))
            .returning(ResponseSpec.json(Status.Ok -> "peachy", obj("anAnotherObject" -> obj("aNumberField" -> number(123)))))
            .returning(ResponseSpec.json(Status.Accepted -> "peachy", obj("anAnotherObject" -> obj("aNumberField" -> number(123)))))
            .returning(Status.Forbidden -> "no way jose")
            .taggedWith("tag3")
            .taggedWith("tag1")
            .at(Get) / "echo" / Path.string("message") bindTo ((s: String) => Echo(s)))
        .withRoute(
          RouteSpec("a post endpoint")
            .consuming(APPLICATION_ATOM_XML, APPLICATION_SVG_XML)
            .producing(APPLICATION_JSON)
            .returning(ResponseSpec.json(Status.Forbidden -> "no way jose", obj("aString" -> Argo.JsonFormat.string("a message of some kind"))))
            .taking(Query.required.int("query"))
            .body(customBody)
            .taggedWith("tag1")
            .taggedWith(TagInfo("tag2", "description of tag"), TagInfo("tag2", "description of tag"))
            .at(Post) / "echo" / Path.string("message") bindTo ((s: String) => Echo(s)))
        .withRoute(
          RouteSpec("a friendly endpoint")
            .taking(Query.required.boolean("query", "description of the query"))
            .body(Body.form(FormField.required.int("form", "description of the form")))
            .at(Get) / "welcome" / Path.string("firstName") / "bertrand" / Path.string("secondName") bindTo ((x: String, y: String, z: String) => Echo(x, y, z)))

      val expected = parse(Source.fromInputStream(this.getClass.getResourceAsStream(s"$name.json")).mkString)

      val actual = Await.result(module.toService(Request("/basepath"))).contentString
//      println(Argo.JsonFormat.pretty(parse(actual)))
      parse(actual) shouldBe expected
    }

    it("can build 400") {
      val response = statusAndContentFrom(renderer.badRequest(Seq(ExtractionError(Query.required.string("bob"), "missing"))))
      response._1 shouldBe Status.BadRequest
      parse(response._2).getStringValue("message") shouldBe "Missing/invalid parameters"
    }

    it("can build 404") {
      val response = statusAndContentFrom(renderer.notFound(Request()))
      response._1 shouldBe Status.NotFound
      parse(response._2).getStringValue("message") shouldBe "No route found on this path. Have you used the correct HTTP verb?"
    }

  }
} 
Example 66
Source File: SiteMapModuleRendererTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.renderers

import java.net.URL

import com.twitter.finagle.Service
import com.twitter.finagle.http.Method._
import com.twitter.finagle.http.Status.{BadRequest, NotFound, Ok}
import com.twitter.finagle.http.path.Root
import com.twitter.finagle.http.{Method, Request, Response}
import com.twitter.util.Future
import io.fintrospect.util.HttpRequestResponseUtil.statusAndContentFrom
import io.fintrospect.{NoSecurity, RouteSpec, ServerRoute}
import org.scalatest.{FunSpec, Matchers}

class SiteMapModuleRendererTest extends FunSpec with Matchers {

  it("renders 400") {
    new SiteMapModuleRenderer(new URL("http://fintrospect.io")).badRequest(Nil).status shouldBe BadRequest
  }

  it("renders 404") {
    new SiteMapModuleRenderer(new URL("http://fintrospect.io")).notFound(Request()).status shouldBe NotFound
  }

  it("should describe only GET endpoints of module as a sitemap") {
    val rsp = new SiteMapModuleRenderer(new URL("http://fintrospect.io")).description(Root / "bob", NoSecurity, Seq(
      endpointFor(Get),
      endpointFor(Post),
      endpointFor(Delete),
      endpointFor(Put),
      endpointFor(Options),
      endpointFor(Connect),
      endpointFor(Head),
      endpointFor(Trace)
    ))

    val (status, content) = statusAndContentFrom(rsp)
    status shouldBe Ok
    content shouldBe <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
      <url>
        <loc>
          http://fintrospect.io/bob/GET
        </loc>
      </url>
    </urlset>.toString()
  }

  private def endpointFor(method: Method): ServerRoute[Request, Response] = {
    RouteSpec().at(method) / method.toString() bindTo Service.mk[Request, Response]((r) => Future(Response()))
  }
} 
Example 67
Source File: StrictContentTypeNegotiationTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.util

import com.twitter.finagle.Service
import com.twitter.finagle.http.Status.{NotAcceptable, Ok}
import com.twitter.finagle.http.{Request, Response}
import com.twitter.util.Await.result
import com.twitter.util.Future
import io.fintrospect.ContentType
import io.fintrospect.ContentTypes.{APPLICATION_ATOM_XML, APPLICATION_JSON, TEXT_HTML}
import io.fintrospect.formats.ResponseBuilder
import org.scalatest.{FunSpec, Matchers}

class StrictContentTypeNegotiationTest extends FunSpec with Matchers {

  describe("StrictContentTypeNegotiation") {
    it("on no match, return 406") {
      val r = result(StrictContentTypeNegotiation(serviceForType(APPLICATION_ATOM_XML))(requestWithAcceptHeaders(APPLICATION_JSON.value)))
      r.status shouldBe NotAcceptable
    }

    it("when there are no accept header set, just chooses the first type") {
      val r = result(StrictContentTypeNegotiation(serviceForType(APPLICATION_ATOM_XML), serviceForType(APPLICATION_JSON))(requestWithAcceptHeaders()))
      r.status shouldBe Ok
      r.headerMap("Content-Type") should startWith(APPLICATION_ATOM_XML.value)
    }

    it("when there is a wildcard set, just chooses the first type") {
      val r = result(StrictContentTypeNegotiation(serviceForType(APPLICATION_ATOM_XML), serviceForType(APPLICATION_JSON))(requestWithAcceptHeaders("**;q=0.5")))
      r.status shouldBe Ok
      r.headerMap("Content-Type") should startWith(TEXT_HTML.value)
    }
  }

  private def serviceForType(contentType: ContentType): (ContentType, Service[Request, Response]) =
    contentType -> Service.mk[Request, Response] { r => Future(ResponseBuilder.HttpResponse(contentType).build()) }


  private def requestWithAcceptHeaders(headers: String*): Request = {
    val request = Request()
    headers.foreach(value => request.headerMap.add("Accept", value))
    request
  }
} 
Example 68
Source File: MultiBodyTypeTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.util

import com.twitter.finagle.Service
import com.twitter.finagle.http.Status.Ok
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.util.Await.result
import com.twitter.util.Future
import io.fintrospect.ContentType
import io.fintrospect.ContentTypes.APPLICATION_JSON
import io.fintrospect.parameters.Body
import org.scalatest.{FunSpec, Matchers}

class MultiBodyTypeTest extends FunSpec with Matchers {

  val xmlBody = Body.xml()
  val xmlAccepting = MultiBodyType(xmlBody -> Service.mk { req: Request => Future(Response(Ok)) })

  describe("MultiBodyType") {
    it("on no match, reject with 415") {
      val r = result(xmlAccepting(requestFromContentType(APPLICATION_JSON)))
      r.status shouldBe Status.UnsupportedMediaType
    }

    it("when there are no content-type header set, reject with 415") {
      val r = result(xmlAccepting(requestFromContentType()))
      r.status shouldBe Status.UnsupportedMediaType
    }

    it("when there is an exact (case insensitive) match on a content type, use that service") {
      val r = Request()
      r.headerMap("Content-Type") = "application/xml"
      r.contentString = "<xml/>"
      val resp = result(xmlAccepting(r))
      resp.status shouldBe Ok
    }
  }

  def requestFromContentType(headers: ContentType*): Request = {
    val request = Request()
    headers.foreach(value => request.headerMap.add("Content-Type", value.value))
    request
  }
} 
Example 69
Source File: OverridableHttpServiceTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.testing

import com.twitter.finagle.Service
import com.twitter.finagle.http.Status.{Accepted, Conflict}
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.util.{Await, Future}
import org.scalatest.{FunSpec, Matchers}

class OverridableHttpServiceTest extends FunSpec with Matchers {

  val originalStatus = Conflict

  val overridableHttpService = new OverridableHttpService[Request](Service.mk { r: Request => Future(Response(originalStatus)) })
  it("will serve routes that are passed to it") {
    statusShouldBe(originalStatus)
  }

  it("can override status") {
    overridableHttpService.respondWith(Accepted)
    statusShouldBe(Accepted)
  }

  private def statusShouldBe(expected: Status): Unit = {
    Await.result(overridableHttpService.service(Request())).status shouldBe expected
  }
} 
Example 70
Source File: TestHttpServerTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.testing

import com.twitter.finagle.http.Method.Get
import com.twitter.finagle.http.Status.{Accepted, Conflict}
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.finagle.{Http, Service}
import com.twitter.util.{Await, Future}
import io.fintrospect.RouteSpec
import org.scalatest.{BeforeAndAfterEach, FunSpec, Matchers}


class TestHttpServerTest extends FunSpec with Matchers with BeforeAndAfterEach {
  val originalStatus = Conflict

  private val server = new TestHttpServer(9888, RouteSpec().at(Get) bindTo Service.mk { r: Request => Future(Response(originalStatus)) })

  override def beforeEach() = {
    Await.result(server.start())
  }

  override def afterEach() = {
    Await.result(server.stop())
  }

  it("will serve routes that are passed to it") {
    statusShouldBe(originalStatus)
  }

  it("can override status") {
    server.respondWith(Accepted)
    statusShouldBe(Accepted)
  }

  private def statusShouldBe(expected: Status): Unit = {
    Await.result(Http.newService("localhost:9888", "")(Request())).status shouldBe expected
  }
} 
Example 71
Source File: App.scala    From finagle-metrics   with MIT License 5 votes vote down vote up
import com.codahale.metrics.ConsoleReporter
import com.twitter.finagle.{ Http, Service }
import com.twitter.finagle.metrics.MetricsStatsReceiver
import com.twitter.finagle.http.{ Request, Response, Status }
import com.twitter.io.Charsets
import com.twitter.server.TwitterServer
import com.twitter.util.{ Await, Future }
import java.util.concurrent.TimeUnit

object App extends TwitterServer {

  val service = new Service[Request, Response] {
    def apply(request: Request) = {
      val response = Response(request.version, Status.Ok)
      response.contentString = "hello"
      Future.value(response)
    }
  }

  val reporter = ConsoleReporter
    .forRegistry(MetricsStatsReceiver.metrics)
    .convertRatesTo(TimeUnit.SECONDS)
    .convertDurationsTo(TimeUnit.MILLISECONDS)
    .build

  def main() = {
    val server = Http.serve(":8080", service)
    reporter.start(5, TimeUnit.SECONDS)

    onExit { server.close() }

    Await.ready(server)
  }

} 
Example 72
Source File: ProcessReportHandler.scala    From finagle-microservice-sample   with Apache License 2.0 5 votes vote down vote up
import com.twitter.finagle.Service
import com.twitter.finagle.http.Response
import com.twitter.util.Future
import org.jboss.netty.handler.codec.http.HttpResponseStatus
import reports.ReportProcessor

class ProcessReportHandler(reportProcessor: ReportProcessor) extends Service[AuthorizedRequest, Response] {

  val processReportUrl = "/report/([0-9]+)/process".r

  override def apply(req: AuthorizedRequest): Future[Response] = req.request.path match {
    case processReportUrl(processId) =>
      reportProcessor.processReport(processId.toInt) map { _ =>
        val response = Response(req.request.getProtocolVersion(), HttpResponseStatus.OK)
        response.setContentTypeJson()
        response.setContentString(
          s"""
            {
              "processId": $processId,
              "processed": true
            }
          """)

        response
      } rescue {
        case _ => Future.value(Response(req.request.getProtocolVersion(), HttpResponseStatus.INTERNAL_SERVER_ERROR))
      }
    case _ => Future.value(Response(req.request.getProtocolVersion(), HttpResponseStatus.NOT_FOUND))
  }

} 
Example 73
Source File: AuthenticationFilter.scala    From finagle-microservice-sample   with Apache License 2.0 5 votes vote down vote up
import com.twitter.finagle.{Service, Filter}
import com.twitter.finagle.http.{Response, Request}
import com.twitter.util.Future
import data.UserData
import org.jboss.netty.handler.codec.http.HttpHeaders.Names
import org.jboss.netty.handler.codec.http.HttpResponseStatus

class AuthenticationFilter(loginService: LoginService) extends Filter[Request, Response, AuthorizedRequest, Response] {

  override def apply(request: Request, continue: Service[AuthorizedRequest, Response]): Future[Response] = {
    if(request.headers().contains(Names.AUTHORIZATION)) {
      //for simplicity, we assume the Authorization request header is in the format "username:password"
      request.headers().get(Names.AUTHORIZATION).split(":") match {
        case Array(username, password) =>
          if(loginService.login(username, password)) {
            val authorizedRequest = AuthorizedRequest(request, UserData(username, List("guest")))
            continue(authorizedRequest) //continue to the next service/filter
          } else unauthorized(request)
        case _ => unauthorized(request)
      }
    } else unauthorized(request)
  }

  def unauthorized(request: Request): Future[Response] =
    Future.value(Response(request.getProtocolVersion(), HttpResponseStatus.UNAUTHORIZED))
} 
Example 74
Source File: TwitterFutureIOMonad.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.monad

import language.experimental.macros
import com.twitter.util.Future
import io.getquill.context.Context
import com.twitter.util.Return
import scala.util.Success
import com.twitter.util.Throw
import scala.util.Failure
import com.twitter.util.Try
import io.getquill.{ Query, Action, ActionReturning, BatchAction }

trait TwitterFutureIOMonad extends IOMonad {
  this: Context[_, _] =>

  type Result[T] = Future[T]

  def runIO[T](quoted: Quoted[T]): IO[RunQuerySingleResult[T], Effect.Read] = macro IOMonadMacro.runIO
  def runIO[T](quoted: Quoted[Query[T]]): IO[RunQueryResult[T], Effect.Read] = macro IOMonadMacro.runIO
  def runIO(quoted: Quoted[Action[_]]): IO[RunActionResult, Effect.Write] = macro IOMonadMacro.runIO
  def runIO[T](quoted: Quoted[ActionReturning[_, T]]): IO[RunActionReturningResult[T], Effect.Write] = macro IOMonadMacro.runIO
  def runIO(quoted: Quoted[BatchAction[Action[_]]]): IO[RunBatchActionResult, Effect.Write] = macro IOMonadMacro.runIO
  def runIO[T](quoted: Quoted[BatchAction[ActionReturning[_, T]]]): IO[RunBatchActionReturningResult[T], Effect.Write] = macro IOMonadMacro.runIO

  case class Run[T, E <: Effect](f: () => Result[T]) extends IO[T, E]

  def performIO[T](io: IO[T, _], transactional: Boolean = false): Result[T] =
    io match {
      case FromTry(t) => Future.const(Try(t.get))
      case Run(f)     => f()
      case Sequence(in, cbf) =>
        Future.collect(in.map(performIO(_)).toSeq)
          .map(r => cbf().++=(r).result)
      case TransformWith(a, fA) =>
        performIO(a)
          .liftToTry.map {
            case Return(v) => Success(v)
            case Throw(t)  => Failure(t)
          }
          .flatMap(v => performIO(fA(v)))
      case Transactional(io) =>
        performIO(io, transactional = true)
    }
} 
Example 75
Source File: ProductFinaglePostgresSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.finagle.postgres

import io.getquill.context.sql.ProductSpec
import com.twitter.util.Await
import com.twitter.util.Future
import io.getquill.context.sql.Id

class ProductFinaglePostgresSpec extends ProductSpec {

  val context = testContext
  import testContext._

  def await[T](r: Future[T]) = Await.result(r)

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

  "Product" - {
    "Insert multiple products" in {
      val inserted = await(Future.collect(productEntries.map(product => testContext.run(productInsert(lift(product))))))
      val product = await(testContext.run(productById(lift(inserted(2))))).head
      product.description mustEqual productEntries(2).description
      product.id mustEqual inserted(2)
    }
    "Single insert product" in {
      val inserted = await(testContext.run(productSingleInsert))
      val product = await(testContext.run(productById(lift(inserted)))).head
      product.description mustEqual "Window"
      product.id mustEqual inserted
    }

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

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

    "Single product insert with a method quotation" in {
      val prd = Product(0L, "test3", 3L)
      val inserted = await(testContext.run(productInsert(lift(prd))))
      val returnedProduct = await(testContext.run(productById(lift(inserted)))).head
      returnedProduct.description mustEqual "test3"
      returnedProduct.sku mustEqual 3L
      returnedProduct.id mustEqual inserted
    }

    "Single insert with value class" in {
      case class Product(id: Id, description: String, sku: Long)
      val prd = Product(Id(0L), "test2", 2L)
      val q1 = quote {
        query[Product].insert(_.sku -> lift(prd.sku), _.description -> lift(prd.description)).returning(_.id)
      }
      await(testContext.run(q1)) mustBe a[Id]
    }

    "supports casts from string to number" - {
      "toInt" in {
        case class Product(id: Long, description: String, sku: Int)
        val queried = await {
          testContext.run {
            query[Product].filter(_.sku == lift("1004").toInt)
          }
        }.head
        queried.sku mustEqual 1004L
      }
      "toLong" in {
        val queried = await {
          testContext.run {
            query[Product].filter(_.sku == lift("1004").toLong)
          }
        }.head
        queried.sku mustEqual 1004L
      }
    }
  }
} 
Example 76
Source File: CaseClassQueryFinagleSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.finagle.postgres

import com.twitter.util.{ Await, Future }
import io.getquill.context.sql.CaseClassQuerySpec
import org.scalatest.matchers.should.Matchers._

class CaseClassQueryFinagleSpec extends CaseClassQuerySpec {

  val context = testContext
  import testContext._

  def await[T](future: Future[T]) = Await.result(future)

  override def beforeAll =
    await {
      testContext.transaction {
        for {
          _ <- testContext.run(query[Contact].delete)
          _ <- testContext.run(query[Address].delete)
          _ <- testContext.run(liftQuery(peopleEntries).foreach(e => peopleInsert(e)))
          _ <- testContext.run(liftQuery(addressEntries).foreach(e => addressInsert(e)))
        } yield {}
      }
    }

  "Example 1 - Single Case Class Mapping" in {
    await(testContext.run(`Ex 1 CaseClass Record Output`)) should contain theSameElementsAs (`Ex 1 CaseClass Record Output expected result`)
  }
  "Example 1A - Single Case Class Mapping" in {
    await(testContext.run(`Ex 1A CaseClass Record Output`)) should contain theSameElementsAs `Ex 1 CaseClass Record Output expected result`
  }
  "Example 1B - Single Case Class Mapping" in {
    await(testContext.run(`Ex 1B CaseClass Record Output`)) should contain theSameElementsAs `Ex 1 CaseClass Record Output expected result`
  }

  "Example 2 - Single Record Mapped Join" in {
    await(testContext.run(`Ex 2 Single-Record Join`)) should contain theSameElementsAs `Ex 2 Single-Record Join expected result`
  }

  "Example 3 - Inline Record as Filter" in {
    await(testContext.run(`Ex 3 Inline Record Usage`)) should contain theSameElementsAs `Ex 3 Inline Record Usage exepected result`
  }
} 
Example 77
Source File: DepartmentsFinaglePostgresSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.finagle.postgres

import com.twitter.util.Await
import com.twitter.util.Future

import io.getquill.context.sql.DepartmentsSpec

class DepartmentsPostgresMysqlSpec extends DepartmentsSpec {

  val context = testContext
  import testContext._

  def await[T](future: Future[T]) = Await.result(future)

  override def beforeAll =
    await {
      testContext.transaction {
        for {
          _ <- testContext.run(query[Department].delete)
          _ <- testContext.run(query[Employee].delete)
          _ <- testContext.run(query[Task].delete)

          _ <- testContext.run(liftQuery(departmentEntries).foreach(e => departmentInsert(e)))
          _ <- testContext.run(liftQuery(employeeEntries).foreach(e => employeeInsert(e)))
          _ <- testContext.run(liftQuery(taskEntries).foreach(e => taskInsert(e)))
        } yield {}
      }
    }

  "Example 8 - nested naive" in {
    await(testContext.run(`Example 8 expertise naive`(lift(`Example 8 param`)))) mustEqual `Example 8 expected result`
  }

  "Example 9 - nested db" in {
    await(testContext.run(`Example 9 expertise`(lift(`Example 9 param`)))) mustEqual `Example 9 expected result`
  }
} 
Example 78
Source File: OnConflictFinagleSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.finagle.postgres

import com.twitter.util.{ Await, Future }
import io.getquill.context.sql.OnConflictSpec

class OnConflictFinagleSpec extends OnConflictSpec {
  val ctx = testContext
  import ctx._

  def await[T](future: Future[T]) = Await.result(future)

  override protected def beforeAll(): Unit = {
    await(ctx.run(qr1.delete))
    ()
  }

  "ON CONFLICT DO NOTHING" in {
    import `onConflictIgnore`._
    await(ctx.run(testQuery1)) mustEqual res1
    await(ctx.run(testQuery2)) mustEqual res2
    await(ctx.run(testQuery3)) mustEqual res3
  }

  "ON CONFLICT (i) DO NOTHING" in {
    import `onConflictIgnore(_.i)`._
    await(ctx.run(testQuery1)) mustEqual res1
    await(ctx.run(testQuery2)) mustEqual res2
    await(ctx.run(testQuery3)) mustEqual res3
  }

  "ON CONFLICT (i) DO UPDATE ..." in {
    import `onConflictUpdate(_.i)((t, e) => ...)`._
    await(ctx.run(testQuery(e1))) mustEqual res1
    await(ctx.run(testQuery(e2))) mustEqual res2
    await(ctx.run(testQuery(e3))) mustEqual res3
    await(ctx.run(testQuery4)) mustEqual res4
  }
} 
Example 79
Source File: PeopleFinaglePostgresSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.finagle.postgres

import com.twitter.util.Await
import com.twitter.util.Future

import io.getquill.context.sql.PeopleSpec

class PeopleFinaglePostgresSpec extends PeopleSpec {

  val context = testContext
  import testContext._

  def await[T](future: Future[T]) = Await.result(future)

  override def beforeAll =
    await {
      testContext.transaction {
        for {
          _ <- testContext.run(query[Couple].delete)
          _ <- testContext.run(query[Person].filter(_.age > 0).delete)
          _ <- testContext.run(liftQuery(peopleEntries).foreach(e => peopleInsert(e)))
          _ <- testContext.run(liftQuery(couplesEntries).foreach(e => couplesInsert(e)))
        } yield {}
      }
    }

  "Example 1 - differences" in {
    await(testContext.run(`Ex 1 differences`)) mustEqual `Ex 1 expected result`
  }

  "Example 2 - range simple" in {
    await(testContext.run(`Ex 2 rangeSimple`(lift(`Ex 2 param 1`), lift(`Ex 2 param 2`)))) mustEqual `Ex 2 expected result`
  }

  "Examples 3 - satisfies" in {
    await(testContext.run(`Ex 3 satisfies`)) mustEqual `Ex 3 expected result`
  }

  "Examples 4 - satisfies" in {
    await(testContext.run(`Ex 4 satisfies`)) mustEqual `Ex 4 expected result`
  }

  "Example 5 - compose" in {
    await(testContext.run(`Ex 5 compose`(lift(`Ex 5 param 1`), lift(`Ex 5 param 2`)))) mustEqual `Ex 5 expected result`
  }

  "Example 6 - predicate 0" in {
    await(testContext.run(satisfies(eval(`Ex 6 predicate`)))) mustEqual `Ex 6 expected result`
  }

  "Example 7 - predicate 1" in {
    await(testContext.run(satisfies(eval(`Ex 7 predicate`)))) mustEqual `Ex 7 expected result`
  }

  "Example 8 - contains empty" in {
    await(testContext.run(`Ex 8 and 9 contains`(liftQuery(`Ex 8 param`)))) mustEqual `Ex 8 expected result`
  }

  "Example 9 - contains non empty" in {
    await(testContext.run(`Ex 8 and 9 contains`(liftQuery(`Ex 9 param`)))) mustEqual `Ex 9 expected result`
  }
} 
Example 80
Source File: TransactionSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.finagle.postgres

import com.twitter.util.{ Await, Future, Throw }

import io.getquill.context.sql.ProductSpec

class TransactionSpec extends ProductSpec {
  val context = testContext
  import context._

  def await[T](future: Future[T]) = Await.result(future)

  val p = Product(0L, "Scala Compiler", 1L)

  "If outer transaction fails, inner transactions shouldn't commit" in {
    val id: Long = await {
      context.transaction {
        for {
          id <- context.transaction {
            context.run(productInsert(lift(p)))
          }
          Throw(_) <- context.transaction {
            context.run(quote {
              query[Product].insert(lift(p.copy(id = id)))
            }).liftToTry
          }
        } yield id
      }
    }
    // Since a query inside a transaction failed, the outermost transaction had
    // to rollback.
    val res: List[Product] = await { context.run(productById(lift(id))) }
    res mustEqual List()
  }

  "Transaction inside transaction should not open a new client" in {
    val res: Product = await {
      context.transaction {
        for {
          id: Long <- context.run(productInsert(lift(p)))
          // A subtransaction should have access to the previous queries of an
          // outer transaction.
          res: List[Product] <- context.transaction {
            context.run(productById(lift(id)))
          }
        } yield res.head
      }
    }
    res mustEqual p.copy(id = res.id)
  }

  override def beforeAll = {
    await(context.run(quote { query[Product].delete }))
    ()
  }
} 
Example 81
Source File: TwitterFutureIOMonad.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.monad

import language.experimental.macros
import com.twitter.util.Future
import io.getquill.context.Context
import com.twitter.util.Try
import scala.collection.compat._
import io.getquill.{ Query, Action, BatchAction, ActionReturning }

trait TwitterFutureIOMonad extends IOMonad {
  this: Context[_, _] =>

  type Result[T] = Future[T]

  def runIO[T](quoted: Quoted[T]): IO[RunQuerySingleResult[T], Effect.Read] = macro IOMonadMacro.runIO
  def runIO[T](quoted: Quoted[Query[T]]): IO[RunQueryResult[T], Effect.Read] = macro IOMonadMacro.runIO
  def runIO(quoted: Quoted[Action[_]]): IO[RunActionResult, Effect.Write] = macro IOMonadMacro.runIO
  def runIO[T](quoted: Quoted[ActionReturning[_, T]]): IO[RunActionReturningResult[T], Effect.Write] = macro IOMonadMacro.runIO
  def runIO(quoted: Quoted[BatchAction[Action[_]]]): IO[RunBatchActionResult, Effect.Write] = macro IOMonadMacro.runIO
  def runIO[T](quoted: Quoted[BatchAction[ActionReturning[_, T]]]): IO[RunBatchActionReturningResult[T], Effect.Write] = macro IOMonadMacro.runIO

  case class Run[T, E <: Effect](f: () => Result[T]) extends IO[T, E]

  def performIO[T](io: IO[T, _], transactional: Boolean = false): Result[T] =
    io match {
      case FromTry(t) => Future.const(Try.fromScala(t))
      case Run(f)     => f()
      case Sequence(in, cbf) =>
        Future.collect(in.iterator.map(performIO(_)).iterator.to(Seq))
          .map(r => cbf.newBuilder.++=(r).result)
      case TransformWith(a, fA) =>
        performIO(a)
          .liftToTry.map(_.asScala)
          .flatMap(v => performIO(fA(v)))
      case Transactional(io) =>
        performIO(io, transactional = true)
    }
} 
Example 82
Source File: PeopleFinagleMysqlSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.finagle.mysql

import com.twitter.util.Await
import com.twitter.util.Future

import io.getquill.context.sql.PeopleSpec

class PeopleFinagleMysqlSpec extends PeopleSpec {

  val context = testContext
  import testContext._

  def await[T](future: Future[T]) = Await.result(future)

  override def beforeAll =
    await {
      testContext.transaction {
        for {
          _ <- testContext.run(query[Couple].delete)
          _ <- testContext.run(query[Person].filter(_.age > 0).delete)
          _ <- testContext.run(liftQuery(peopleEntries).foreach(e => peopleInsert(e)))
          _ <- testContext.run(liftQuery(couplesEntries).foreach(e => couplesInsert(e)))
        } yield {}
      }
    }

  "Example 1 - differences" in {
    await(testContext.run(`Ex 1 differences`)) mustEqual `Ex 1 expected result`
  }

  "Example 2 - range simple" in {
    await(testContext.run(`Ex 2 rangeSimple`(lift(`Ex 2 param 1`), lift(`Ex 2 param 2`)))) mustEqual `Ex 2 expected result`
  }

  "Examples 3 - satisfies" in {
    await(testContext.run(`Ex 3 satisfies`)) mustEqual `Ex 3 expected result`
  }

  "Examples 4 - satisfies" in {
    await(testContext.run(`Ex 4 satisfies`)) mustEqual `Ex 4 expected result`
  }

  "Example 5 - compose" in {
    await(testContext.run(`Ex 5 compose`(lift(`Ex 5 param 1`), lift(`Ex 5 param 2`)))) mustEqual `Ex 5 expected result`
  }

  "Example 6 - predicate 0" in {
    await(testContext.run(satisfies(eval(`Ex 6 predicate`)))) mustEqual `Ex 6 expected result`
  }

  "Example 7 - predicate 1" in {
    await(testContext.run(satisfies(eval(`Ex 7 predicate`)))) mustEqual `Ex 7 expected result`
  }

  "Example 8 - contains empty" in {
    await(testContext.run(`Ex 8 and 9 contains`(liftQuery(`Ex 8 param`)))) mustEqual `Ex 8 expected result`
  }

  "Example 9 - contains non empty" in {
    await(testContext.run(`Ex 8 and 9 contains`(liftQuery(`Ex 9 param`)))) mustEqual `Ex 9 expected result`
  }
} 
Example 83
Source File: CaseClassQueryFinagleSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.finagle.mysql

import com.twitter.util.{ Await, Future }
import io.getquill.context.sql.CaseClassQuerySpec
import org.scalatest.matchers.should.Matchers._

class CaseClassQueryFinagleSpec extends CaseClassQuerySpec {

  val context = testContext
  import testContext._

  def await[T](future: Future[T]) = Await.result(future)

  override def beforeAll =
    await {
      testContext.transaction {
        for {
          _ <- testContext.run(query[Contact].delete)
          _ <- testContext.run(query[Address].delete)
          _ <- testContext.run(liftQuery(peopleEntries).foreach(e => peopleInsert(e)))
          _ <- testContext.run(liftQuery(addressEntries).foreach(e => addressInsert(e)))
        } yield {}
      }
    }

  "Example 1 - Single Case Class Mapping" in {
    await(testContext.run(`Ex 1 CaseClass Record Output`)) should contain theSameElementsAs `Ex 1 CaseClass Record Output expected result`
  }
  "Example 1A - Single Case Class Mapping" in {
    await(testContext.run(`Ex 1A CaseClass Record Output`)) should contain theSameElementsAs `Ex 1 CaseClass Record Output expected result`
  }
  "Example 1B - Single Case Class Mapping" in {
    await(testContext.run(`Ex 1B CaseClass Record Output`)) should contain theSameElementsAs `Ex 1 CaseClass Record Output expected result`
  }

  "Example 2 - Single Record Mapped Join" in {
    await(testContext.run(`Ex 2 Single-Record Join`)) should contain theSameElementsAs `Ex 2 Single-Record Join expected result`
  }

  "Example 3 - Inline Record as Filter" in {
    await(testContext.run(`Ex 3 Inline Record Usage`)) should contain theSameElementsAs `Ex 3 Inline Record Usage exepected result`
  }
} 
Example 84
Source File: OnConflictFinagleSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.finagle.mysql

import com.twitter.util.{ Await, Future }
import io.getquill.context.sql.OnConflictSpec

class OnConflictFinagleSpec extends OnConflictSpec {
  val ctx = testContext
  import ctx._

  def await[T](future: Future[T]) = Await.result(future)

  override protected def beforeAll(): Unit = {
    await(ctx.run(qr1.delete))
    ()
  }

  "INSERT IGNORE" in {
    import `onConflictIgnore`._
    await(ctx.run(testQuery1)) mustEqual res1
    await(ctx.run(testQuery2)) mustEqual res2
    await(ctx.run(testQuery3)) mustEqual res3
  }

  "ON DUPLICATE KEY UPDATE i=i " in {
    import `onConflictIgnore(_.i)`._
    await(ctx.run(testQuery1)) mustEqual res1
    await(ctx.run(testQuery2)) mustEqual res2 + 1
    await(ctx.run(testQuery3)) mustEqual res3
  }

  "ON DUPLICATE KEY UPDATE ..." in {
    import `onConflictUpdate((t, e) => ...)`._
    await(ctx.run(testQuery(e1))) mustEqual res1
    await(ctx.run(testQuery(e2))) mustEqual res2 + 1
    await(ctx.run(testQuery(e3))) mustEqual res3 + 1
    await(ctx.run(testQuery4)) mustEqual res4
  }
} 
Example 85
Source File: DepartmentsFinagleMysqlSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.finagle.mysql

import com.twitter.util.Await
import com.twitter.util.Future

import io.getquill.context.sql.DepartmentsSpec

class DepartmentsFinagleMysqlSpec extends DepartmentsSpec {

  val context = testContext
  import testContext._

  def await[T](future: Future[T]) = Await.result(future)

  override def beforeAll =
    await {
      testContext.transaction {
        for {
          _ <- testContext.run(query[Department].delete)
          _ <- testContext.run(query[Employee].delete)
          _ <- testContext.run(query[Task].delete)

          _ <- testContext.run(liftQuery(departmentEntries).foreach(e => departmentInsert(e)))
          _ <- testContext.run(liftQuery(employeeEntries).foreach(e => employeeInsert(e)))
          _ <- testContext.run(liftQuery(taskEntries).foreach(e => taskInsert(e)))
        } yield {}
      }
    }

  "Example 8 - nested naive" in {
    await(testContext.run(`Example 8 expertise naive`(lift(`Example 8 param`)))) mustEqual `Example 8 expected result`
  }

  "Example 9 - nested db" in {
    await(testContext.run(`Example 9 expertise`(lift(`Example 9 param`)))) mustEqual `Example 9 expected result`
  }

  "Example 9 - streamed result" in {
    await(testContext.stream(`Example 9 expertise`(lift(`Example 9 param`))).flatMap(_.toSeq())) mustEqual `Example 9 expected result`
  }
} 
Example 86
Source File: ProductFinagleMysqlSpec.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.finagle.mysql

import io.getquill.context.sql.ProductSpec
import com.twitter.util.Await
import com.twitter.util.Future
import io.getquill.context.sql.Id

class ProductFinagleMysqlSpec extends ProductSpec {

  val context = testContext
  import testContext._

  def await[T](r: Future[T]) = Await.result(r)

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

  "Product" - {
    "Insert multiple products" in {
      val inserted = await(Future.collect(productEntries.map(product => testContext.run(productInsert(lift(product))))))
      val product = await(testContext.run(productById(lift(inserted(2))))).head
      product.description mustEqual productEntries(2).description
      product.id mustEqual inserted(2)
    }
    "Single insert product" in {
      val inserted = await(testContext.run(productSingleInsert))
      val product = await(testContext.run(productById(lift(inserted)))).head
      product.description mustEqual "Window"
      product.id mustEqual inserted
    }

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

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

    "Single product insert with a method quotation" in {
      val prd = Product(0L, "test3", 3L)
      val inserted = await(testContext.run(productInsert(lift(prd))))
      val returnedProduct = await(testContext.run(productById(lift(inserted)))).head
      returnedProduct.description mustEqual "test3"
      returnedProduct.sku mustEqual 3L
      returnedProduct.id mustEqual inserted
    }

    "Single insert with value class" in {
      case class Product(id: Id, description: String, sku: Long)
      val prd = Product(Id(0L), "test2", 2L)
      val q1 = quote {
        query[Product].insert(_.sku -> lift(prd.sku), _.description -> lift(prd.description)).returningGenerated(_.id)
      }
      await(testContext.run(q1)) mustBe a[Id]
    }

    "supports casts from string to number" - {
      "toInt" in {
        case class Product(id: Long, description: String, sku: Int)
        val queried = await {
          testContext.run {
            query[Product].filter(_.sku == lift("1004").toInt)
          }
        }.head
        queried.sku mustEqual 1004L
      }
      "toLong" in {
        val queried = await {
          testContext.run {
            query[Product].filter(_.sku == lift("1004").toLong)
          }
        }.head
        queried.sku mustEqual 1004L
      }
    }
  }
} 
Example 87
Source File: OkTestClient.scala    From quill   with Apache License 2.0 5 votes vote down vote up
package io.getquill.context.finagle.mysql

import com.twitter.concurrent.AsyncStream
import com.twitter.finagle.mysql
import com.twitter.util.{ Future, Time }
import java.util.concurrent.atomic.AtomicInteger
import com.twitter.finagle.mysql.Transactions
import com.twitter.finagle.mysql.Session
import com.twitter.finagle.mysql.Client

class OkTestClient extends mysql.Client with mysql.Transactions {
  val methodCount = new AtomicInteger

  val ok = mysql.OK(0, 0, 0, 0, "")

  override def query(sql: String): Future[mysql.Result] = {
    methodCount.incrementAndGet()
    Future(ok)
  }

  override def select[T](sql: String)(f: mysql.Row => T): Future[Seq[T]] = {
    methodCount.incrementAndGet()
    Future(Seq.empty)
  }
  override def prepare(sql: String): mysql.PreparedStatement = {
    methodCount.incrementAndGet()
    new mysql.PreparedStatement {
      override def apply(params: mysql.Parameter*): Future[mysql.Result] = Future(ok)
    }
  }
  override def cursor(sql: String): mysql.CursoredStatement = {
    methodCount.incrementAndGet()
    new mysql.CursoredStatement {
      override def apply[T](rowsPerFetch: Int, params: mysql.Parameter*)(f: mysql.Row => T): Future[mysql.CursorResult[T]] = Future {
        new mysql.CursorResult[T] {
          override def stream: AsyncStream[T] = AsyncStream.empty
          override def close(deadline: Time): Future[Unit] = Future.Unit
        }
      }
    }
  }

  override def ping(): Future[Unit] = {
    methodCount.incrementAndGet()
    Future.Unit
  }

  override def transaction[T](f: mysql.Client => Future[T]): Future[T] = {
    f(this)
  }
  override def transactionWithIsolation[T](isolationLevel: mysql.IsolationLevel)(f: mysql.Client => Future[T]): Future[T] = {
    f(this)
  }

  override def close(deadline: Time): Future[Unit] = Future.Unit

  def session[T](f: Client with Transactions with Session => Future[T]): Future[T] = {
    ???
  }
} 
Example 88
Source File: HelloWorld.scala    From finch-101   with Apache License 2.0 5 votes vote down vote up
package i.f.workshop.finagle

import com.twitter.finagle.http.{Request, Response}
import com.twitter.finagle.{ListeningServer, Http, Service}
import com.twitter.io.Buf
import com.twitter.util.{Await, Future}

object HelloWorld extends App {

  val service: Service[Request, Response] = new Service[Request, Response] {
    def apply(req: Request): Future[Response] = {
      val rep: Response = Response()
      rep.content = Buf.Utf8("Hello, World!")

      Future.value(rep)
    }
  }

  val server: ListeningServer = Http.server.serve(":8081", service)
  Await.ready(server)
} 
Example 89
Source File: MesosMasterClient.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos

import com.mesosphere.cosmos.http.RequestSession
import io.lemonlabs.uri.Uri
import io.lemonlabs.uri.dsl._
import com.twitter.finagle.Service
import com.twitter.finagle.http.Request
import com.twitter.finagle.http.Response
import com.twitter.util.Future
import io.lemonlabs.uri.QueryString
import org.jboss.netty.handler.codec.http.HttpMethod

class MesosMasterClient(
  mesosUri: Uri,
  client: Service[Request, Response]
) extends ServiceClient(mesosUri) {

  def tearDownFramework(
    frameworkId: String
  )(
    implicit session: RequestSession
  ): Future[thirdparty.mesos.master.model.MesosFrameworkTearDownResponse] = {
    val formData = QueryString.fromPairs("frameworkId" -> frameworkId).toString()
    
    val encodedString = formData.toString.substring(1)
    val uri = "master" / "teardown"
    client(postForm(uri, encodedString))
      .map(validateResponseStatus(HttpMethod.POST, uri, _))
      .map { _ => thirdparty.mesos.master.model.MesosFrameworkTearDownResponse() }
  }

  def getFrameworks(
    name: String
  )(
    implicit session: RequestSession
  ): Future[List[thirdparty.mesos.master.model.Framework]] = {
    val uri = "master" / "frameworks"
    val request = get(uri)
    client(request).flatMap(
      decodeTo[thirdparty.mesos.master.model.MasterState](HttpMethod.GET, uri, _)
    ).map { state =>
      state.frameworks.filter(_.name == name)
    }
  }
} 
Example 90
Source File: PackageSearchHandler.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.handler

import com.mesosphere.cosmos.finch.EndpointHandler
import com.mesosphere.cosmos.http.RequestSession
import com.mesosphere.cosmos.repository.PackageCollection
import com.mesosphere.cosmos.rpc
import com.twitter.util.Future

private[cosmos] final class PackageSearchHandler(
  packageCollection: PackageCollection
) extends EndpointHandler[rpc.v1.model.SearchRequest, rpc.v1.model.SearchResponse] {

  override def apply(
    request: rpc.v1.model.SearchRequest
  )(implicit session: RequestSession): Future[rpc.v1.model.SearchResponse] = {
    packageCollection
      .search(request.query)
      .map(rpc.v1.model.SearchResponse(_))
  }
} 
Example 91
Source File: PackageRenderHandler.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.handler

import com.mesosphere.cosmos.error.ServiceMarathonTemplateNotFound
import com.mesosphere.cosmos.finch.EndpointHandler
import com.mesosphere.cosmos.http.RequestSession
import com.mesosphere.cosmos.render._
import com.mesosphere.cosmos.repository.PackageCollection
import com.mesosphere.cosmos.rpc
import com.mesosphere.universe
import com.mesosphere.universe.bijection.UniverseConversions._
import com.twitter.bijection.Conversion.asMethod
import com.twitter.util.Future

private[cosmos] final class PackageRenderHandler(
  packageCollection: PackageCollection
) extends EndpointHandler[rpc.v1.model.RenderRequest, rpc.v1.model.RenderResponse] {

  override def apply(
    request: rpc.v1.model.RenderRequest
  )(
    implicit session: RequestSession
  ): Future[rpc.v1.model.RenderResponse] = {
    packageCollection
      .getPackageByPackageVersion(
        request.packageName,
        request.packageVersion.as[Option[universe.v3.model.Version]]
      )
      .flatMap { case (pkg, uri) =>

        val packageConfig = PackageDefinitionRenderer.renderMarathonV2App(uri, pkg, request.options, request.appId)

        packageConfig match {
          case Some(renderedMarathonJson) =>
            Future.value(rpc.v1.model.RenderResponse(renderedMarathonJson))
          case None =>
            Future.exception(ServiceMarathonTemplateNotFound(pkg.name, pkg.version).exception)
        }
      }
  }
} 
Example 92
Source File: PackageInstallHandler.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.handler

import com.mesosphere.cosmos.MarathonPackageRunner
import com.mesosphere.cosmos.error.CosmosException
import com.mesosphere.cosmos.error.PackageAlreadyInstalled
import com.mesosphere.cosmos.error.ServiceAlreadyStarted
import com.mesosphere.cosmos.http.RequestSession
import com.mesosphere.cosmos.render.PackageDefinitionRenderer
import com.mesosphere.cosmos.repository.PackageCollection
import com.mesosphere.cosmos.repository.rewriteUrlWithProxyInfo
import com.mesosphere.cosmos.rpc
import com.mesosphere.cosmos.service.CustomPackageManagerRouter
import com.mesosphere.universe
import com.mesosphere.universe.bijection.UniverseConversions._
import com.twitter.bijection.Conversion.asMethod
import com.twitter.util.Future

private[cosmos] final class PackageInstallHandler(
  packageCollection: PackageCollection,
  packageRunner: MarathonPackageRunner,
  customPackageManagerRouter: CustomPackageManagerRouter
) extends CustomEndpointHandler[rpc.v1.model.InstallRequest, rpc.v2.model.InstallResponse] {

  private[this] lazy val logger = org.slf4j.LoggerFactory.getLogger(getClass)

  override def apply(
    request: rpc.v1.model.InstallRequest
  )(
    implicit session: RequestSession
  ): Future[rpc.v2.model.InstallResponse] = {
    orElseGet(tryCustomPackageManager(request)) {
      packageCollection
        .getPackageByPackageVersion(
          request.packageName,
          request.packageVersion.as[Option[universe.v3.model.Version]]
        )
        .flatMap { case (pkg, sourceUri) =>
          PackageDefinitionRenderer.renderMarathonV2App(
            sourceUri,
            pkg,
            request.options,
            request.appId
          ) match {
            case Some(renderedMarathonJson) =>
              packageRunner.launch(renderedMarathonJson)
                .map { runnerResponse =>
                  rpc.v2.model.InstallResponse(
                    packageName = pkg.name,
                    packageVersion = pkg.version,
                    appId = Some(runnerResponse.id),
                    postInstallNotes = pkg.postInstallNotes,
                    cli = pkg.rewrite(rewriteUrlWithProxyInfo(session.originInfo), identity).cli
                  )
                }
                .handle {
                  case CosmosException(ServiceAlreadyStarted(_), _, _) =>
                    throw PackageAlreadyInstalled().exception
                }
            case None =>
              Future {
                rpc.v2.model.InstallResponse(
                  packageName = pkg.name,
                  packageVersion = pkg.version,
                  appId = None,
                  postInstallNotes = pkg.postInstallNotes,
                  cli = pkg.rewrite(rewriteUrlWithProxyInfo(session.originInfo), identity).cli
                )
              }
          }
        }
    }
  }

  override def tryCustomPackageManager(
    request: rpc.v1.model.InstallRequest
  )(
    implicit session: RequestSession
  ): Future[Option[rpc.v2.model.InstallResponse]] = {
    customPackageManagerRouter.getCustomPackageManagerId(
      request.managerId,
      Option(request.packageName),
      request.packageVersion.as[Option[universe.v3.model.Version]],
      None
    ).flatMap {
      case Some((Some(managerId), _, _)) if !managerId.isEmpty =>
        logger.debug(s"Request [$request] requires a custom manager: [$managerId]")
        customPackageManagerRouter.callCustomPackageInstall(request, managerId).map(Some(_))
      case _ => Future(None)
    }
  }
} 
Example 93
Source File: CustomEndpointHandler.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.handler

import com.mesosphere.cosmos.finch.EndpointHandler
import com.mesosphere.cosmos.http.RequestSession
import com.twitter.util.Future

trait CustomEndpointHandler[A, B] extends EndpointHandler[A, B] {
  def tryCustomPackageManager(
    a : A
  )(
    implicit session: RequestSession
  ) : Future[Option[B]]

  def orElseGet(b : Future[Option[B]])(alternative: => Future[B]): Future[B] = {
    b.flatMap {
      case None => alternative
      case Some(x) => Future(x)
    }
  }
} 
Example 94
Source File: ServiceDescribeHandler.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.handler

import com.mesosphere.cosmos.AdminRouter
import com.mesosphere.cosmos.http.RequestSession
import com.mesosphere.cosmos.render.PackageDefinitionRenderer
import com.mesosphere.cosmos.repository.PackageCollection
import com.mesosphere.cosmos.repository.rewriteUrlWithProxyInfo
import com.mesosphere.cosmos.rpc
import com.mesosphere.cosmos.service.CustomPackageManagerRouter
import com.mesosphere.cosmos.thirdparty.marathon.model.MarathonApp
import com.mesosphere.universe
import com.mesosphere.universe.v4.model.PackageDefinition
import com.twitter.util.Future
import io.circe.JsonObject

private[cosmos] final class ServiceDescribeHandler(
  adminRouter: AdminRouter,
  packageCollection: PackageCollection,
  customPackageManagerRouter: CustomPackageManagerRouter
) extends CustomEndpointHandler[rpc.v1.model.ServiceDescribeRequest, rpc.v1.model.ServiceDescribeResponse] {

  private[this] lazy val logger = org.slf4j.LoggerFactory.getLogger(getClass)

  override def apply(
    request: rpc.v1.model.ServiceDescribeRequest
  )(
    implicit session: RequestSession
  ): Future[rpc.v1.model.ServiceDescribeResponse] = {
    orElseGet(tryCustomPackageManager(request)) {
      for {
        marathonAppResponse <- adminRouter.getApp(request.appId)
        packageDefinition <- getPackageDefinition(marathonAppResponse.app)
        upgradesTo <- packageCollection.upgradesTo(packageDefinition.name, packageDefinition.version)
        downgradesTo <- packageCollection.downgradesTo(packageDefinition)
      } yield {
        val userProvidedOptions = marathonAppResponse.app.serviceOptions
        rpc.v1.model.ServiceDescribeResponse(
          `package` = packageDefinition,
          upgradesTo = upgradesTo,
          downgradesTo = downgradesTo,
          resolvedOptions = getResolvedOptions(packageDefinition, userProvidedOptions),
          userProvidedOptions = userProvidedOptions
        )
      }
    }
  }

  private def getPackageDefinition(
    app: MarathonApp)(implicit
    session: RequestSession
  ): Future[universe.v4.model.PackageDefinition] = {
    app.packageDefinition
      .map(pkg => Future.value(pkg.rewrite(rewriteUrlWithProxyInfo(session.originInfo), identity)))
      .getOrElse {
        val (name, version) =
          app.packageName.flatMap(name => app.packageVersion.map(name -> _))
            .getOrElse(throw new IllegalStateException(
              "The name and version of the service were not found in the labels"))
        packageCollection
          .getPackageByPackageVersion(name, Some(version))
          .map(_._1)
      }
  }

  private def getResolvedOptions(
    packageDefinition: PackageDefinition,
    serviceOptions: Option[JsonObject]
  ): Option[JsonObject] = {
    serviceOptions.map { userSuppliedOptions =>
      PackageDefinitionRenderer.mergeDefaultAndUserOptions(packageDefinition, Some(userSuppliedOptions))
    }
  }

  override def tryCustomPackageManager(
    request: rpc.v1.model.ServiceDescribeRequest
  )(
    implicit session: RequestSession
  ): Future[Option[rpc.v1.model.ServiceDescribeResponse]] = {
    customPackageManagerRouter.getCustomPackageManagerId(
      request.managerId,
      request.packageName,
      request.packageVersion,
      Some(request.appId)
    ).flatMap {
      case Some((Some(managerId), Some(pkgName), Some(pkgVersion))) if !managerId.isEmpty =>
        logger.debug(s"Request [$request] requires a custom manager: [$managerId]")
        customPackageManagerRouter.callCustomServiceDescribe(
          request,
          managerId,
          pkgName,
          pkgVersion
        ).map(Some(_))
      case _ => Future(None)
    }
  }
} 
Example 95
Source File: CapabilitiesHandler.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.handler

import com.mesosphere.cosmos.finch.EndpointHandler
import com.mesosphere.cosmos.http.RequestSession
import com.mesosphere.cosmos.rpc.v1.model.{CapabilitiesResponse, Capability}
import com.twitter.util.Future

private[cosmos] final class CapabilitiesHandler
  extends EndpointHandler[Unit, CapabilitiesResponse] {

  private[this] val response = CapabilitiesResponse(
    List(Capability("PACKAGE_MANAGEMENT"),
         Capability("SUPPORT_CLUSTER_REPORT"),
         Capability("METRONOME"),
         Capability("LOGGING"),
         Capability("LOGGING_V2")))

  override def apply(v1: Unit)(implicit
    session: RequestSession
  ): Future[CapabilitiesResponse] = {
    Future.value(response)
  }

} 
Example 96
Source File: ListHandler.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.handler

import com.mesosphere.cosmos.AdminRouter
import com.mesosphere.cosmos.converter.Response._
import com.mesosphere.cosmos.finch.EndpointHandler
import com.mesosphere.cosmos.http.RequestSession
import com.mesosphere.cosmos.repository.rewriteUrlWithProxyInfo
import com.mesosphere.cosmos.rpc
import com.mesosphere.cosmos.thirdparty
import com.twitter.bijection.Conversion.asMethod
import com.twitter.util.Future

private[cosmos] final class ListHandler(
  adminRouter: AdminRouter
) extends EndpointHandler[rpc.v1.model.ListRequest, rpc.v1.model.ListResponse] {

  override def apply(
    request: rpc.v1.model.ListRequest
  )(
    implicit session: RequestSession
  ): Future[rpc.v1.model.ListResponse] = {
    for {
      apps <- getApplications(adminRouter, request)
    } yield {
      rpc.v1.model.ListResponse(
        apps.sortBy(install => (install.packageInformation.packageDefinition.name, install.appId))
      )
    }
  }

  private[this] def getApplications(
    adminRouter: AdminRouter,
    request: rpc.v1.model.ListRequest
  )(
    implicit session: RequestSession
  ): Future[Seq[rpc.v1.model.Installation]] = {

    def satisfiesRequest(app: thirdparty.marathon.model.MarathonApp): Boolean = {
      // corner case: packageReleaseVersion will be None if parsing the label fails
      app.packageName.exists { pkgName =>
        request.packageName.forall(_ == pkgName) && request.appId.forall(_ == app.id)
      }
    }

    adminRouter.listApps().map { response =>
      response.apps.flatMap { app =>
        if (satisfiesRequest(app)) {
          decodeInstalledPackageInformation(app).map { info =>
            rpc.v1.model.Installation(
              app.id,
              info
            )
          }
        } else None
      }
    }
  }

  private[this] def decodeInstalledPackageInformation(
    app: thirdparty.marathon.model.MarathonApp
  )(
    implicit session: RequestSession
  ): Option[rpc.v1.model.InstalledPackageInformation] = {
    app.packageDefinition
      .map(
        _.rewrite(rewriteUrlWithProxyInfo(session.originInfo), identity)
         .as[rpc.v1.model.InstalledPackageInformation]
      )
  }

} 
Example 97
Source File: PackageRepositoryDeleteHandler.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.handler

import cats.data.Ior
import com.mesosphere.cosmos.error.RepoNameOrUriMissing
import com.mesosphere.cosmos.finch.EndpointHandler
import com.mesosphere.cosmos.http.RequestSession
import com.mesosphere.cosmos.repository.PackageCollection
import com.mesosphere.cosmos.repository.PackageSourcesStorage
import com.mesosphere.cosmos.rpc
import com.twitter.util.Future

private[cosmos] final class PackageRepositoryDeleteHandler(
  sourcesStorage: PackageSourcesStorage,
  repositories: PackageCollection
) extends EndpointHandler[rpc.v1.model.PackageRepositoryDeleteRequest, rpc.v1.model.PackageRepositoryDeleteResponse] {

  import PackageRepositoryDeleteHandler._

  override def apply(
    request: rpc.v1.model.PackageRepositoryDeleteRequest
  )(
    implicit session: RequestSession
  ): Future[rpc.v1.model.PackageRepositoryDeleteResponse] = {
    val nameOrUri = optionsToIor(request.name, request.uri).getOrElse(
      throw RepoNameOrUriMissing().exception
    )
    sourcesStorage
      .delete(nameOrUri)
      .map(rpc.v1.model.PackageRepositoryDeleteResponse(_))
      .foreach(_ => repositories.invalidateAll())
  }

}

object PackageRepositoryDeleteHandler {

  private def optionsToIor[A, B](aOpt: Option[A], bOpt: Option[B]): Option[Ior[A, B]] = {
    (aOpt, bOpt) match {
      case (Some(a), Some(b)) => Some(Ior.Both(a, b))
      case (Some(a), _) => Some(Ior.Left(a))
      case (_, Some(b)) => Some(Ior.Right(b))
      case _ => None
    }
  }

} 
Example 98
Source File: PackageRepositoryAddHandler.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.handler

import com.mesosphere.cosmos.error.CosmosException
import com.mesosphere.cosmos.error.UniverseClientHttpError
import com.mesosphere.cosmos.error.UnsupportedRepositoryUri
import com.mesosphere.cosmos.finch.EndpointHandler
import com.mesosphere.cosmos.http.RequestSession
import com.mesosphere.cosmos.repository.PackageCollection
import com.mesosphere.cosmos.repository.PackageSourcesStorage
import com.mesosphere.cosmos.repository.UniverseClient
import com.mesosphere.cosmos.rpc
import com.twitter.util.Future
import io.netty.handler.codec.http.HttpResponseStatus

private[cosmos] final class PackageRepositoryAddHandler(
  sourcesStorage: PackageSourcesStorage,
  universeClient: UniverseClient,
  repositories: PackageCollection
) extends EndpointHandler[rpc.v1.model.PackageRepositoryAddRequest, rpc.v1.model.PackageRepositoryAddResponse] {

  override def apply(
    request: rpc.v1.model.PackageRepositoryAddRequest
  )(
    implicit session: RequestSession
  ): Future[rpc.v1.model.PackageRepositoryAddResponse] = {
    val repository = rpc.v1.model.PackageRepository(request.name, request.uri)
    request.uri.schemeOption match {
      case Some("http") | Some("https") =>
        checkThatRepoWorks(repository)
          .flatMap { _ =>
            sourcesStorage
              .add(request.index, repository)
              .map(rpc.v1.model.PackageRepositoryAddResponse(_))
              .foreach(_ => repositories.invalidateAll())
          }
      case _ => throw UnsupportedRepositoryUri(request.uri).exception
    }
  }

  private[this] def checkThatRepoWorks(
    repository: rpc.v1.model.PackageRepository
  )(
    implicit session: RequestSession
  ): Future[Unit] = {
    
    universeClient(repository).unit.handle {
      case ce: CosmosException =>
        ce.error match {
          case uce : UniverseClientHttpError =>
            throw ce.copy(error = uce.copy(status = HttpResponseStatus.BAD_REQUEST))
          case _ => throw ce
        }
    }
  }

} 
Example 99
Source File: ListVersionsHandler.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.handler

import com.mesosphere.cosmos.finch.EndpointHandler
import com.mesosphere.cosmos.http.RequestSession
import com.mesosphere.cosmos.repository.PackageCollection
import com.mesosphere.cosmos.rpc
import com.mesosphere.universe
import com.mesosphere.universe.bijection.UniverseConversions._
import com.twitter.bijection.Conversion.asMethod
import com.twitter.util.Future

final class ListVersionsHandler(
  packageCollection: PackageCollection
) extends EndpointHandler[rpc.v1.model.ListVersionsRequest, rpc.v1.model.ListVersionsResponse] {

  override def apply(request: rpc.v1.model.ListVersionsRequest)(implicit
    session: RequestSession
  ): Future[rpc.v1.model.ListVersionsResponse] = {
    packageCollection.getPackagesByPackageName(request.packageName)
      .map { packages =>
        val versions = packages.map { pkg =>
          val packageVersion = pkg.version.as[universe.v2.model.PackageDetailsVersion]
          val releaseVersion = pkg.releaseVersion.as[universe.v2.model.ReleaseVersion]

          packageVersion -> releaseVersion
        }

        rpc.v1.model.ListVersionsResponse(versions.toMap)
      }
  }
} 
Example 100
Source File: package.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos

import cats.syntax.apply._
import cats.instances.option._
import com.mesosphere.cosmos.http.RequestSession
import com.mesosphere.cosmos.repository.PackageCollection
import com.mesosphere.cosmos.thirdparty.marathon.model.MarathonApp
import com.mesosphere.universe
import io.lemonlabs.uri.Uri
import com.twitter.util.Future

package object handler {

  type PackageWithSource = (universe.v4.model.PackageDefinition, Uri)

  def getPackageWithSourceOrThrow(
      packageCollection: PackageCollection,
      marathonApp: MarathonApp
  )(
      implicit session: RequestSession
  ): Future[PackageWithSource] = {
    getPackageWithSource(packageCollection, marathonApp).map(_.getOrElse {
      throw new IllegalStateException(
        "Unable to retrieve the old package definition"
      )
    })
  }

  def getPackageWithSource(
      packageCollection: PackageCollection,
      marathonApp: MarathonApp
  )(
      implicit session: RequestSession
  ): Future[Option[PackageWithSource]] = {
    orElse(Future.value(getStoredPackageWithSource(marathonApp)))(
      lookupPackageWithSource(packageCollection, marathonApp)
    )
  }

  def traverse[A, B](a: Option[A])(fun: A => Future[B]): Future[Option[B]] = {
    a.map(fun) match {
      case None    => Future.value(None)
      case Some(v) => v.map(Some(_))
    }
  }

  private def getStoredPackageWithSource(
      marathonApp: MarathonApp
  ): Option[PackageWithSource] = {
    (
      marathonApp.packageDefinition,
      marathonApp.packageRepository.map(_.uri)
    ).tupled
  }

  private def lookupPackageWithSource(
      packageCollection: PackageCollection,
      marathonApp: MarathonApp
  )(
      implicit session: RequestSession
  ): Future[Option[PackageWithSource]] = {
    traverse(getPackageCoordinate(marathonApp)) {
      case (name, version) =>
        packageCollection.getPackageByPackageVersion(name, Some(version))
    }
  }

  private def getPackageCoordinate(
      marathonApp: MarathonApp
  ): Option[(String, universe.v3.model.Version)] = {
    (
      marathonApp.packageName,
      marathonApp.packageVersion
    ).tupled
  }

  private def orElse[A](
      f1: Future[Option[A]]
  )(
      f2: => Future[Option[A]]
  ): Future[Option[A]] = {
    f1.flatMap {
      case r: Some[A] => Future.value(r)
      case None       => f2
    }
  }
} 
Example 101
Source File: PackageDescribeHandler.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.handler

import com.mesosphere.cosmos.finch.EndpointHandler
import com.mesosphere.cosmos.http.RequestSession
import com.mesosphere.cosmos.repository.PackageCollection
import com.mesosphere.cosmos.repository.rewriteUrlWithProxyInfo
import com.mesosphere.cosmos.rpc
import com.mesosphere.universe
import com.mesosphere.universe.bijection.UniverseConversions._
import com.twitter.bijection.Conversion.asMethod
import com.twitter.util.Future

private[cosmos] final class PackageDescribeHandler(
  packageCollection: PackageCollection
) extends EndpointHandler[rpc.v1.model.DescribeRequest, universe.v4.model.PackageDefinition] {

  override def apply(
    request: rpc.v1.model.DescribeRequest
  )(
    implicit session: RequestSession
  ): Future[universe.v4.model.PackageDefinition] = {
    val packageInfo = packageCollection.getPackageByPackageVersion(
      request.packageName,
      request.packageVersion.as[Option[universe.v3.model.Version]]
    )

    packageInfo.map { case (pkg, _) =>
      pkg.rewrite(rewriteUrlWithProxyInfo(session.originInfo), identity)
    }
  }

} 
Example 102
Source File: MarathonPackageRunner.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos

import _root_.io.circe.JsonObject
import com.mesosphere.cosmos.circe.Decoders.decode
import com.mesosphere.cosmos.error.MarathonBadGateway
import com.mesosphere.cosmos.error.MarathonBadResponse
import com.mesosphere.cosmos.error.MarathonGenericError
import com.mesosphere.cosmos.error.ServiceAlreadyStarted
import com.mesosphere.cosmos.http.RequestSession
import com.mesosphere.cosmos.thirdparty.marathon.model.MarathonApp
import com.mesosphere.cosmos.thirdparty.marathon.model.MarathonError
import com.mesosphere.error.ResultOps
import com.twitter.finagle.http.Status
import com.twitter.util.Future
import io.netty.handler.codec.http.HttpResponseStatus
import scala.util.Failure
import scala.util.Success
import scala.util.Try

final class MarathonPackageRunner(adminRouter: AdminRouter) {

  private[this] lazy val logger = org.slf4j.LoggerFactory.getLogger(getClass)

  
  def launch(renderedConfig: JsonObject)(implicit session: RequestSession): Future[MarathonApp] = {
    adminRouter.createApp(renderedConfig)
      .map { response =>
        response.status match {
          case Status.Conflict =>
            throw ServiceAlreadyStarted().exception
          case status if (400 until 500).contains(status.code) =>
            logger.warn(s"Marathon returned [${status.code}]: " +
              s"${trimContentForPrinting(response.contentString)}")
            Try(decode[MarathonError](response.contentString).getOrThrow) match {
              case Success(marathonError) =>
                throw MarathonBadResponse(marathonError).exception
              case Failure(_) =>
                throw MarathonGenericError(HttpResponseStatus.valueOf(status.code)).exception
            }
          case status if (500 until 600).contains(status.code) =>
            logger.warn(s"Marathon returned [${status.code}]: " +
              s"${trimContentForPrinting(response.contentString)}")
            throw MarathonBadGateway(HttpResponseStatus.valueOf(status.code)).exception
          case _ =>
            decode[MarathonApp](response.contentString).getOrThrow
        }
      }
  }

} 
Example 103
Source File: ServiceUpdater.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos

import _root_.io.circe.JsonObject
import com.mesosphere.cosmos.circe.Decoders.parse
import com.mesosphere.cosmos.http.RequestSession
import com.mesosphere.cosmos.thirdparty.marathon.model.AppId
import com.mesosphere.error.ResultOps
import com.twitter.finagle.http.Response
import com.twitter.finagle.http.Status
import com.twitter.util.Future

final class ServiceUpdater(adminRouter: AdminRouter) {
  def update(
    appId: AppId,
    renderedConfig: JsonObject
  )(implicit session: RequestSession): Future[String] = {
    adminRouter.update(appId, renderedConfig).map { response: Response =>
      response.status match {
        case Status.Ok =>
          parse(response.contentString).getOrThrow.hcursor.get[String]("deploymentId").right.get
        case _ =>
          // TODO: Why do we do this?
          throw new Error(response.contentString)
      }
    }
  }
}

object ServiceUpdater {
  def apply(adminRouter: AdminRouter): ServiceUpdater = {
    new ServiceUpdater(adminRouter)
  }
} 
Example 104
Source File: EndpointHandler.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.finch

import com.mesosphere.cosmos.http.RequestSession
import com.twitter.finagle.http.Fields
import com.twitter.finagle.http.Status
import com.twitter.util.Future
import com.twitter.util.Throw
import io.circe.Json
import io.circe.syntax._
import io.finch._

abstract class EndpointHandler[Request, Response](successStatus: Status = Status.Ok) {

  private[this] val logger = org.slf4j.LoggerFactory.getLogger(getClass)

  final def apply(context: EndpointContext[Request, Response]): Future[Output[Json]] = {
    apply(context.requestBody)(context.session)
      .respond {
        case Throw(e) =>
          logger.warn(s"Processing [${context.requestBody}] resulted in : ${e.getMessage}")
        case _ => ()
      }
      .map { response =>
        Output
          .payload(response.asJson(context.responseEncoder.encoder), successStatus)
          .withHeader(Fields.ContentType -> context.responseEncoder.mediaType(response).show)
      }
  }

  def apply(request: Request)(implicit session: RequestSession): Future[Response]

} 
Example 105
Source File: TimedMulticastService.scala    From diffy   with GNU Affero General Public License v3.0 5 votes vote down vote up
package ai.diffy.proxy

import com.twitter.finagle.Service
import com.twitter.util.{Future, Try}

class TimedMulticastService[-A, +B](services: Seq[Service[A, B]])
  extends Service[A, Seq[(Try[B], Long, Long)]]
{
  def apply(request: A): Future[Seq[(Try[B], Long, Long)]] =
    services.foldLeft[Future[Seq[(Try[B], Long, Long)]]](Future.Nil){ case (acc, service) =>
      acc flatMap { responseTriesWithTiming =>
        val start = System.currentTimeMillis()
        val nextResponse: Future[Try[B]] = service(request).liftToTry
        nextResponse map { responseTry: Try[B] => {
          val end  = System.currentTimeMillis()
          responseTriesWithTiming ++ Seq((responseTry, start, end))
        }}
      }
    }
} 
Example 106
Source File: HttpDifferenceProxy.scala    From diffy   with GNU Affero General Public License v3.0 5 votes vote down vote up
package ai.diffy.proxy

import java.net.SocketAddress

import ai.diffy.analysis.{DifferenceAnalyzer, InMemoryDifferenceCollector, JoinedDifferences}
import ai.diffy.lifter.{HttpLifter, Message}
import com.twitter.finagle.http.{Method, Request, Response}
import com.twitter.finagle.{Filter, Http}
import com.twitter.util.{Future, StorageUnit, Try}

object HttpDifferenceProxy {
  def requestHostHeaderFilter(host: String) =
    Filter.mk[Request, Response, Request, Response] { (req, svc) =>
      req.host(host)
      svc(req)
    }
}

trait HttpDifferenceProxy extends DifferenceProxy {
  import HttpDifferenceProxy._
  val servicePort: SocketAddress
  val lifter = new HttpLifter(settings.excludeHttpHeadersComparison, settings.resourceMatcher)

  override type Req = Request
  override type Rep = Response
  override type Srv = HttpService

  override def serviceFactory(serverset: String, label: String) =
    HttpService(requestHostHeaderFilter(serverset) andThen
      Http.client
        .withMaxResponseSize(settings.maxResponseSize)
        .withMaxHeaderSize(settings.maxHeaderSize)
        .newService(serverset, label))

  override lazy val server =
    Http.serve(
      servicePort,
      proxy
    )

  override def liftRequest(req: Request): Future[Message] =
    lifter.liftRequest(req)

  override def liftResponse(resp: Try[Response]): Future[Message] =
    lifter.liftResponse(resp)
}

object SimpleHttpDifferenceProxy {
  
case class SimpleHttpsDifferenceProxy (
   settings: Settings,
   collector: InMemoryDifferenceCollector,
   joinedDifferences: JoinedDifferences,
   analyzer: DifferenceAnalyzer)
  extends HttpDifferenceProxy
{
  import SimpleHttpDifferenceProxy._

  override val servicePort = settings.servicePort

  override val proxy =
    Filter.identity andThenIf
      (!settings.allowHttpSideEffects, httpSideEffectsFilter) andThen
      super.proxy

  override def serviceFactory(serverset: String, label: String) =
    HttpService(
      Http.client
      .withTls(serverset)
      .newService(serverset+":"+settings.httpsPort, label)
    )
} 
Example 107
Source File: SequentialMulticastService.scala    From diffy   with GNU Affero General Public License v3.0 5 votes vote down vote up
package ai.diffy.proxy

import com.twitter.finagle.Service
import com.twitter.util.{Future, Try}

class SequentialMulticastService[-A, +B](
    services: Seq[Service[A, B]])
  extends Service[A, Seq[Try[B]]]
{
  def apply(request: A): Future[Seq[Try[B]]] =
    services.foldLeft[Future[Seq[Try[B]]]](Future.Nil){ case (acc, service) =>
      acc flatMap { responseTries =>
        val nextResponse = service(request).liftToTry
        nextResponse map { responseTry => responseTries ++ Seq(responseTry) }
      }
    }
} 
Example 108
Source File: ThriftDifferenceProxy.scala    From diffy   with GNU Affero General Public License v3.0 5 votes vote down vote up
package ai.diffy.proxy

import java.io.File
import java.util.zip.ZipFile

import ai.diffy.analysis.{DifferenceAnalyzer, InMemoryDifferenceCollector, JoinedDifferences}
import ai.diffy.lifter.{MapLifterPool, Message, ThriftLifter}
import ai.diffy.scrooge.ZippedFileImporter
import com.twitter.finagle.thrift.{ClientId, ThriftClientRequest}
import com.twitter.finagle.tracing.NullTracer
import com.twitter.finagle.{Resolver, Thrift, ThriftMux}
import com.twitter.util.{Future, Try}

import scala.collection.JavaConversions._

case class ThriftDifferenceProxy (
    settings: Settings,
    collector: InMemoryDifferenceCollector,
    joinedDifferences: JoinedDifferences,
    analyzer: DifferenceAnalyzer)
  extends DifferenceProxy
{
  override type Req = ThriftClientRequest
  override type Rep = Array[Byte]
  override type Srv = ThriftService

  private[this] lazy val clientId = new ClientId(settings.clientId)

  override val proxy = super.proxy

  private[this] val zipfile = new ZipFile(new File(settings.pathToThriftJar))

  private[this] val importer =
    ZippedFileImporter(Seq(zipfile))

  private[this] val filenames =
    zipfile.entries.toSeq collect {
      case zipEntry if !zipEntry.isDirectory && zipEntry.getName.endsWith(".thrift") =>
        zipEntry.getName
    }

  val lifter =
    MapLifterPool(
      ThriftLifter.fromImporter(
        importer,
        filenames,
        settings.serviceClass
      )
    )

  override def serviceFactory(serverset: String, label: String) = {
    val client = if (settings.enableThriftMux) {
      ThriftMux.client
        .withClientId(clientId)
        .newClient(serverset, label).toService
    } else {
      val config = if(settings.useFramedThriftTransport) {
        Thrift.client
      } else {
        Thrift.client.withBufferedTransport
      }

      config
        .withNoAttemptTTwitterUpgrade
        .withTracer(NullTracer)
        .withClientId(clientId)
        .newClient(serverset, label)
        .toService
    }

    ThriftService(client, Resolver.eval(serverset))
  }

  override lazy val server = {
    if (settings.enableThriftMux) {
      ThriftMux.serve(
        settings.servicePort,
        proxy map { req: Array[Byte] => new ThriftClientRequest(req, false) }
      )
    } else {
      val config = if(settings.useFramedThriftTransport) {
        Thrift.server
      } else {
        Thrift.server.withBufferedTransport()
      }
      config.withTracer(NullTracer).serve(
        settings.servicePort,
        proxy map { req: Array[Byte] => new ThriftClientRequest(req, false) }
      )
    }
  }

  override def liftRequest(req: ThriftClientRequest): Future[Message] = lifter(req.message)
  override def liftResponse(rep: Try[Array[Byte]]): Future[Message] =
    Future.const(rep) flatMap { lifter(_) }
} 
Example 109
Source File: HttpLifter.scala    From diffy   with GNU Affero General Public License v3.0 5 votes vote down vote up
package ai.diffy.lifter

import ai.diffy.util.ResourceMatcher
import com.twitter.finagle.http.{Request, Response}
import com.twitter.logging.Logger
import com.twitter.util.{Future, Try}


object HttpLifter {


  val ControllerEndpointHeaderName = "X-Action-Name"

  def contentTypeNotSupportedException(contentType: String) = new Exception(s"Content type: $contentType is not supported")
  def contentTypeNotSupportedExceptionFuture(contentType: String) = Future.exception(contentTypeNotSupportedException(contentType))

  case class MalformedJsonContentException(cause: Throwable)
    extends Exception("Malformed Json content")
  {
    initCause(cause)
  }
}

class HttpLifter(excludeHttpHeadersComparison: Boolean, resourceMatcher: Option[ResourceMatcher] = None) {
  import HttpLifter._

  private[this] val log = Logger(classOf[HttpLifter])
  private[this] def headersMap(response: Response): Map[String, Any] = {
    if(!excludeHttpHeadersComparison) {
      val rawHeaders = response.headerMap.toSeq

      val headers = rawHeaders map { case (name, value) =>
        (name.toLowerCase, value)
      } groupBy { _._1} map { case (name, pairs) =>
        name -> (pairs map { _._2 } sorted)
      }

      Map( "headers" -> FieldMap(headers))
    } else Map.empty
  }

  def liftRequest(req: Request): Future[Message] = {
    val headers = req.headerMap

    val canonicalResource = headers
      .get("Canonical-Resource")
      .orElse(resourceMatcher.flatMap(_.resourceName(req.path)))

    val params = req.getParams()
    val body = StringLifter.lift(req.getContentString())
    Future.value(
      Message(
        canonicalResource,
        FieldMap(
          Map(
            "uri" -> req.uri,
            "method" -> req.method,
            "headers" -> headers,
            "params" -> params,
            "body" -> body
          )
        )
      )
    )
  }

  def liftResponse(resp: Try[Response]): Future[Message] = {
    log.debug(s"$resp")
    Future.const(resp) flatMap { r: Response =>

      
      val controllerEndpoint = r.headerMap.get(ControllerEndpointHeaderName)

      val stringContentTry = Try {
        StringLifter.lift(r.getContentString())
      }

      Future.const(stringContentTry map { stringContent =>
        val responseMap = Map(
          r.statusCode.toString -> (Map(
            "content" -> stringContent,
            "chunked" -> r.isChunked
          ) ++ headersMap(r))
        )

        Message(controllerEndpoint, FieldMap(responseMap))
      })

    }
  }
} 
Example 110
Source File: MapLifter.scala    From diffy   with GNU Affero General Public License v3.0 5 votes vote down vote up
package ai.diffy.lifter

import com.twitter.concurrent.NamedPoolThreadFactory
import com.twitter.util.{ExecutorServiceFuturePool, Future, FuturePool}
import java.util.concurrent.{ArrayBlockingQueue, ThreadPoolExecutor, TimeUnit}

case class Message(endpoint: Option[String], result: FieldMap[Any])

trait MapLifter {
  def apply(input: Array[Byte]): Future[Message]
}

object MapLifterPool {
  val QueueSizeDefault = 5

  def apply(mapLifterFactory: => MapLifter) = {
    val executorService =
      new ThreadPoolExecutor(
        3,   // core pool size
        10,  // max pool size
        500, // keep alive time
        TimeUnit.MILLISECONDS,
        new ArrayBlockingQueue[Runnable](10), // work queue
        new NamedPoolThreadFactory("maplifter", makeDaemons = true),
        new ThreadPoolExecutor.AbortPolicy()
      )
    executorService.prestartCoreThread()
    new MapLifterPool(mapLifterFactory, new ExecutorServiceFuturePool(executorService))
  }
}

class MapLifterPool(underlying: MapLifter, futurePool: FuturePool) extends MapLifter {
  override def apply(input: Array[Byte]): Future[Message] =
    (futurePool { underlying(input) }).flatten
} 
Example 111
Source File: JoinedDifferences.scala    From diffy   with GNU Affero General Public License v3.0 5 votes vote down vote up
package ai.diffy.analysis

import javax.inject.Inject

import com.twitter.util.Future
import scala.math.abs

object DifferencesFilterFactory {
  def apply(relative: Double, absolute: Double): JoinedField => Boolean = {
    (field: JoinedField) =>
      field.raw.differences > field.noise.differences &&
        field.relativeDifference > relative &&
        field.absoluteDifference > absolute
  }
}

case class JoinedDifferences @Inject() (raw: RawDifferenceCounter, noise: NoiseDifferenceCounter) {
  def endpoints: Future[Map[String, JoinedEndpoint]] = {
    raw.counter.endpoints map { _.keys } flatMap { eps =>
      Future.collect(
        eps map { ep =>
          endpoint(ep) map { ep -> _ }
        } toSeq
      ) map { _.toMap }
    }
  }

  def endpoint(endpoint: String): Future[JoinedEndpoint] = {
    Future.join(
      raw.counter.endpoint(endpoint),
      raw.counter.fields(endpoint),
      noise.counter.fields(endpoint)
    ) map { case (endpoint, rawFields, noiseFields) =>
      JoinedEndpoint(endpoint, rawFields, noiseFields)
    }
  }
}

case class JoinedEndpoint(
  endpoint: EndpointMetadata,
  original: Map[String, FieldMetadata],
  noise: Map[String, FieldMetadata])
{
  def differences = endpoint.differences
  def total = endpoint.total
  def fields: Map[String, JoinedField] = original map { case (path, field) =>
    path -> JoinedField(endpoint, field, noise.getOrElse(path, FieldMetadata.Empty))
  }
}

case class JoinedField(endpoint: EndpointMetadata, raw: FieldMetadata, noise: FieldMetadata) {
  // the percent difference out of the total # of requests
  def absoluteDifference = abs(raw.differences - noise.differences) / endpoint.total.toDouble * 100
  // the square error between this field's differences and the noisey counterpart's differences
  def relativeDifference = abs(raw.differences - noise.differences) / (raw.differences + noise.differences).toDouble * 100
} 
Example 112
Source File: DifferenceCounter.scala    From diffy   with GNU Affero General Public License v3.0 5 votes vote down vote up
package ai.diffy.analysis

import ai.diffy.compare.Difference
import com.twitter.util.Future

trait EndpointMetadata {
  // number of differences seen at this endpoint
  def differences: Int
  // total # of requests seen for this endpoint
  def total: Int
}

object FieldMetadata {
  val Empty = new FieldMetadata {
    override val differences = 0
    override val weight = 0
  }
}

trait FieldMetadata {
  // number of difference seen for this field
  def differences: Int
  // weight of this field relative to other fields, this number is calculated by counting the
  // number of fields that saw differences on every request that this field saw a difference in
  def weight: Int
}

trait DifferenceCounter {
  def count(endpoint: String, diffs: Map[String, Difference]): Future[Unit]
  def endpoints: Future[Map[String, EndpointMetadata]]
  def endpoint(endpoint: String) = endpoints flatMap { ep => Future { ep(endpoint) } }
  def fields(endpoint: String): Future[Map[String, FieldMetadata]]
  def clear(): Future[Unit]
}

case class RawDifferenceCounter(counter: DifferenceCounter)
case class NoiseDifferenceCounter(counter: DifferenceCounter) 
Example 113
Source File: EmailSender.scala    From diffy   with GNU Affero General Public License v3.0 5 votes vote down vote up
package ai.diffy.util

import com.twitter.logging.Logger
import com.twitter.util.{FuturePool, Future}

import javax.mail._
import javax.mail.internet.{InternetAddress, MimeMessage}
import java.util.Properties

case class SimpleMessage(
  from: String,
  to: String,
  bcc: String,
  subject: String,
  body: String)

class EmailSender(log: Logger, send: MimeMessage => Unit = Transport.send) {
  private[this] val props = new Properties
  props.put("mail.smtp.host", "localhost")
  props.put("mail.smtp.auth", "false")
  props.put("mail.smtp.port", "25")

  private[this] val session = Session.getDefaultInstance(props, null)

  def apply(msg: SimpleMessage): Future[Unit] =
    FuturePool.unboundedPool {
      val message = new MimeMessage(session)
      message.setFrom(new InternetAddress(msg.from))
      message.setRecipients(
        Message.RecipientType.TO,
        InternetAddress.parse(msg.to) map { _.asInstanceOf[Address]}
      )
      message.addRecipients(
        Message.RecipientType.BCC,
        InternetAddress.parse(msg.bcc) map { _.asInstanceOf[Address]}
      )
      message.setSubject(msg.subject)
      message.setContent(msg.body, "text/html; charset=utf-8")
      try {
        send(message)
      } catch { case e =>
        log.error("Failed to send email report. Ensure Diffy can access port 25.")
      }
    }
} 
Example 114
Source File: DiffyProject.scala    From diffy   with GNU Affero General Public License v3.0 5 votes vote down vote up
package ai.diffy.util

import ai.diffy.compare.Difference
import ai.diffy.lifter.JsonLifter
import ai.diffy.proxy.Settings
import com.twitter.finagle.Http
import com.twitter.finagle.http.{Method, Request, Response}
import com.twitter.util.Future

object DiffyProject {
  private[this] sealed trait config {
    val client: Request => Future[Response]
  }
  private[this] object production extends config {
    override val client: Request => Future[Response] =
      Http.client
        .withTls("diffyproject.appspot.com")
        .newService("diffyproject.appspot.com:443")
  }
  private[this] object development extends config {
    override val client: Request => Future[Response] =
      Http.client
        .newService("localhost:7000")
  }

  private[this] val cfg: config = production

  def settings(settings: Settings): Unit ={
    s = settings
    val m = Difference.mkMap(s)
    val ed = m("emailDelay")
    uid = m.updated("emailDelay",ed.toString).updated("artifact", "od.2019.8.27.001")
    log("start")
  }

  private[this] var s :Settings = _
  private[this] var uid :Map[String, Any] = Map.empty

  def log(message: String): Unit = {
    val request = Request(Method.Post, "/stats")
    request.setContentTypeJson()
    request.setContentString(JsonLifter.encode(uid.updated("message", message)))
    cfg.client(request)
  }
} 
Example 115
Source File: ThriftFeatureTest.scala    From diffy   with GNU Affero General Public License v3.0 5 votes vote down vote up
package ai.diffy

import java.io.{File, FileOutputStream}
import java.net.ServerSocket
import java.nio.file.Files
import java.util.zip.{ZipEntry, ZipOutputStream}

import ai.diffy.examples.thrift.ExampleServers
import ai.diffy.proxy.DifferenceProxy
import ai.diffy.thriftscala.Adder
import com.google.inject.Stage
import com.twitter.finagle.http.Status
import com.twitter.finagle.util.DefaultTimer
import com.twitter.finagle.{Http, ThriftMux}
import com.twitter.finatra.http.EmbeddedHttpServer
import com.twitter.inject.Test
import com.twitter.util.{Await, Duration, Future, FuturePool}

import scala.io.Source

class ThriftFeatureTest extends Test {

  def getPort(): Int = {
    val s  = new ServerSocket(0)
    val port = s.getLocalPort
    s.close()
    port
  }

  val env@Seq(p,s,c,d) = Seq.fill(4)(getPort())
  val environment = FuturePool.unboundedPool(ExampleServers.main(env.take(3).map(_.toString).toArray))

  val diffy = new MainService
  lazy val differenceProxy = diffy.injector.instance[DifferenceProxy]


  val thriftFile = new File("src/test/thrift/example.thrift")
  val data = Source.fromInputStream(Files.newInputStream(thriftFile.toPath), "UTF-8").mkString
  val thriftJar = Files.createTempFile("thrift", "jar")
  thriftJar.toFile.deleteOnExit()
  val out = new ZipOutputStream(new FileOutputStream(thriftJar.toFile))
  out.putNextEntry(new ZipEntry(thriftFile.getAbsolutePath))
  out.write(data.getBytes)
  out.closeEntry()
  out.close()

  val server = new EmbeddedHttpServer(
    twitterServer = diffy,
    flags = Map(
      "proxy.port" -> s":$d",
      "candidate" -> s"localhost:$c",
      "master.primary" -> s"localhost:$p",
      "master.secondary" -> s"localhost:$s",
      "serviceName" -> "myThriftService",
      "service.protocol" -> "thrift",
      "thrift.jar" -> thriftJar.toAbsolutePath.toString,
      "thrift.serviceClass" -> "Adder",
      "summary.email" -> "test"
    ),
    stage = Stage.PRODUCTION
  )

  val client = ThriftMux.client.build[Adder.MethodPerEndpoint](s"localhost:$d")

  test("verify startup") {
    server.assertHealthy()
  }

  test("verify DifferenceCollector") {
    assert(differenceProxy.collector.fields.isEmpty)
    Await.result(client.add(1, 1).liftToTry)
    var tries = 0
    while(differenceProxy.outstandingRequests.get() > 0 && tries < 10) {
      Await.result(Future.sleep(Duration.fromSeconds(1))(DefaultTimer))
      tries = tries + 1
    }
    assert(!differenceProxy.collector.fields.isEmpty)
  }

  test("verify present differences via API") {
    val response =
      Await.result(Http.fetchUrl(s"http://${server.externalHttpHostAndPort}/api/1/endpoints/add/stats"))
    assertResult(Status.Ok)(response.status)
    assert(response.getContentString().contains(""""differences":1"""))
  }

  test("verify absent endpoint in API") {
    val response =
      Await.result(Http.fetchUrl(s"http://${server.externalHttpHostAndPort}/api/1/endpoints/subtract/stats"))
    assertResult(Status.Ok)(response.status)
    assertResult("""{"error":"key not found: subtract"}""")(response.getContentString())
  }

} 
Example 116
Source File: ExampleServers.scala    From diffy   with GNU Affero General Public License v3.0 5 votes vote down vote up
package ai.diffy.examples.thrift

import java.net.InetSocketAddress

import ai.diffy.thriftscala._
import com.twitter.finagle.ThriftMux
import com.twitter.util.Future

object ExampleServers {
  def main(args: Array[String]): Unit = {
    val primary = args(0).toInt
    val secondary = args(1).toInt
    val candidate = args(2).toInt

    val baseline = new AdderExample({case (a:Int,b:Int) => a + b})
    val sut = new AdderExample({case (a:Int,b:Int) => a * b})

    ThriftMux.server.serveIface(new InetSocketAddress(primary), baseline)
    ThriftMux.server.serveIface(new InetSocketAddress(secondary), baseline)
    ThriftMux.server.serveIface(new InetSocketAddress(candidate), sut)
  }
}

class AdderExample(f: (Int,Int) => Int) extends Adder.MethodPerEndpoint {
  override def add(a:Int,b:Int)= Future.value(f(a, b))
} 
Example 117
Source File: DifferenceStatsMonitorSpec.scala    From diffy   with GNU Affero General Public License v3.0 5 votes vote down vote up
package ai.diffy.workflow

import ai.diffy.ParentSpec
import ai.diffy.analysis.{DifferenceCounter, EndpointMetadata, RawDifferenceCounter}
import com.twitter.finagle.stats.InMemoryStatsReceiver
import com.twitter.util.{Duration, Future, MockTimer, Time}
import org.junit.runner.RunWith
import org.mockito.Mockito._
import org.scalatest.junit.JUnitRunner

@RunWith(classOf[JUnitRunner])
class DifferenceStatsMonitorSpec extends ParentSpec {
  describe("DifferenceStatsMonitor"){
    val diffCounter = mock[DifferenceCounter]
    val metadata =
      new EndpointMetadata {
        override val differences = 0
        override val total = 0
      }

    val endpoints = Map("endpointName" -> metadata)
    when(diffCounter.endpoints) thenReturn Future.value(endpoints)

    val stats = new InMemoryStatsReceiver
    val timer = new MockTimer
    val monitor = new DifferenceStatsMonitor(RawDifferenceCounter(diffCounter), stats, timer)

    it("must add gauges after waiting a minute"){
      Time.withCurrentTimeFrozen { tc =>
        monitor.schedule()
        timer.tasks.size must be(1)
        stats.gauges.size must be(0)
        tc.advance(Duration.fromMinutes(1))
        timer.tick()
        timer.tasks.size must be(1)
        stats.gauges.size must be(2)
        stats.gauges.keySet map { _.takeRight(2) } must be(Set(Seq("endpointName", "total"), Seq("endpointName", "differences")))
      }
    }
  }
} 
Example 118
Source File: HttpFeatureTest.scala    From diffy   with GNU Affero General Public License v3.0 5 votes vote down vote up
package ai.diffy

import java.net.ServerSocket

import ai.diffy.examples.http.ExampleServers
import ai.diffy.proxy.DifferenceProxy
import com.google.common.collect.ImmutableMap
import com.google.inject.Stage
import com.twitter.finagle.Http
import com.twitter.finagle.http.Status
import com.twitter.finagle.util.DefaultTimer
import com.twitter.finatra.http.EmbeddedHttpServer
import com.twitter.inject.Test
import com.twitter.util.{Await, Duration, Future, FuturePool}

class HttpFeatureTest extends Test {
  def getPort(): Int = {
    val s  = new ServerSocket(0)
    val port = s.getLocalPort
    s.close()
    port
  }

  val env@Seq(p,s,c,d) = Seq.fill(4)(getPort())
  val environment = FuturePool.unboundedPool(ExampleServers.main(env.take(3).map(_.toString).toArray))

  val diffy = new MainService
  lazy val differenceProxy = diffy.injector.instance[DifferenceProxy]

  val server = new EmbeddedHttpServer(
    twitterServer = diffy,
    flags = Map(
      "proxy.port" -> s":$d",
      "candidate" -> s"localhost:$c",
      "master.primary" -> s"localhost:$p",
      "master.secondary" -> s"localhost:$s",
      "serviceName" -> "myHttpService",
      "service.protocol" -> "http",
      "summary.email" ->"test"
    ),
    stage = Stage.PRODUCTION
  )

  test("verify startup") {
    server.assertHealthy()
  }

  test("verify DifferenceCollector") {
    assert(differenceProxy.collector.fields.isEmpty)
    Await.result(Http.fetchUrl(s"http://localhost:$d/json?Twitter").liftToTry)
    var tries = 0
    while(differenceProxy.outstandingRequests.get() > 0 && tries < 10) {
      Await.result(Future.sleep(Duration.fromSeconds(1))(DefaultTimer.twitter))
      tries = tries + 1
    }
    assert(!differenceProxy.collector.fields.isEmpty)
  }

  test("verify present differences via API") {
    val response =
      Await.result(Http.fetchUrl(s"http://${server.externalHttpHostAndPort}/api/1/endpoints/undefined_endpoint/stats"))
    assertResult(Status.Ok)(response.status)
    assert(response.getContentString().contains(""""differences":1"""))
  }

  test("verify absent endpoint in API") {
    val response =
      Await.result(Http.fetchUrl(s"http://${server.externalHttpHostAndPort}/api/1/endpoints/json/stats"))
    assertResult(Status.Ok)(response.status)
    assertResult("""{"error":"key not found: json"}""")(response.getContentString())
  }
  Seq(
    "/api/1/overview",
    "/api/1/report",
    "/api/1/endpoints",
    "/api/1/endpoints/json/stats",
    "/api/1/endpoints/json/fields/result.200.values.value.name.PrimitiveDifference/results",
    "/api/1/endpoints/json/fields/result.200.values.value.name.PrimitiveDifference/results/0"
  ) foreach { endpoint =>
    test(s"ping ${endpoint}") {
      val response =
        Await.result(Http.fetchUrl(s"http://${server.externalHttpHostAndPort}${endpoint}"))
      assertResult(Status.Ok)(response.status)
    }
  }
} 
Example 119
Source File: PeopleService.scala    From finch-template   with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
package com.redbubble.finchtemplate.services.people

import com.redbubble.finchtemplate.fetch.people.PeopleFetcher._
import com.redbubble.finchtemplate.model.Person
import com.redbubble.finchtemplate.model.people.PersonId
import com.redbubble.util.fetch.syntax._
import com.twitter.util.Future

trait PeopleService {
  private implicit lazy val fetchRunner = com.redbubble.finchtemplate.util.fetch.runner

  final def allPeople(): Future[Seq[Person]] = {
    val fetch = allPeopleFetch
    fetch.runF
  }

  final def personDetails(personId: PersonId): Future[Option[Person]] = {
    val fetch = personFetch(personId)
    fetch.runF
  }
}

object PeopleService extends PeopleService 
Example 120
Source File: HDFSStore.scala    From speedo   with Apache License 2.0 5 votes vote down vote up
package com.htc.speedo.caffe

import com.twitter.bijection.Codec
import com.twitter.storehaus.Store
import com.twitter.util.{ Future, Time }

import org.apache.commons.io.IOUtils
import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.fs.Path

object HDFSStore {
  
  val fs = rootDir.getFileSystem(conf)

  // make sure the root directory exists
  fs.mkdirs(rootDir)

  override def get(key: String) = Future {
    Some(new Path(rootDir, key)).filter(fs.exists).flatMap { path =>
      val stream = fs.open(path)
      val bytes = IOUtils.toByteArray(stream)
      stream.close
      codec.invert(bytes).toOption
    }
  }

  override def put(kv: (String, Option[V])) = Future {
    val path = new Path(rootDir, kv._1)
    kv._2 match {
      case None => fs.delete(path, false)
      case Some(v) =>
        val bytes = codec(v)
        val stream = fs.create(path, true)
        stream.write(bytes)
        stream.close
    }
  }

  override def close(time: Time) = Future { fs.close }
} 
Example 121
Source File: EnvRouting.scala    From typed-schema   with Apache License 2.0 5 votes vote down vote up
package ru.tinkoff.tschema.finagle.envRouting

import cats.syntax.semigroup._
import com.twitter
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.finagle.{Service, http}
import com.twitter.util.{Future, Promise}
import monix.eval.Task
import monix.execution.Scheduler
import ru.tinkoff.tschema.finagle.Rejection.Recover
import ru.tinkoff.tschema.finagle._
import ru.tinkoff.tschema.finagle.envRouting.EnvRouting.EnvHttp
import ru.tinkoff.tschema.utils.SubString
import tofu.env.Env

final case class EnvRouting[+R](
    request: http.Request,
    path: CharSequence,
    matched: Int,
    embedded: R
)

object EnvRouting extends EnvInstanceDecl {

  type EnvHttp[R, +A] = Env[EnvRouting[R], A]

  implicit def taskRouted[R]
      : RoutedPlus[EnvHttp[R, *]] with ConvertService[EnvHttp[R, *]] with LiftHttp[EnvHttp[R, *], Env[R, *]] =
    envRoutedAny.asInstanceOf[EnvRoutedConvert[R]]

  implicit def envRunnable[R](implicit
      recover: Recover[EnvHttp[R, *]] = Recover.default[EnvHttp[R, *]]
  ): RunHttp[EnvHttp[R, *], Env[R, *]] =
    response => {
      val handled = response.onErrorRecoverWith { case Rejected(rej) => recover(rej) }
      Env(r => Task.deferAction(implicit sched => Task.delay(execResponse(r, handled, _))))
    }

  private[this] def execResponse[R](r: R, envResponse: EnvHttp[R, Response], request: Request)(implicit
      sc: Scheduler
  ): Future[Response] = {
    val promise = Promise[Response]
    val routing = EnvRouting(request, SubString(request.path), 0, r)

    val cancelable = envResponse.run(routing).runAsync {
      case Right(res) => promise.setValue(res)
      case Left(ex)   =>
        val resp    = Response(Status.InternalServerError)
        val message = Option(ex.getLocalizedMessage).getOrElse(ex.toString)
        resp.setContentString(message)
        promise.setValue(resp)
    }

    promise.setInterruptHandler { case _ => cancelable.cancel() }

    promise
  }
}

private[finagle] class EnvInstanceDecl {

  protected trait EnvRoutedConvert[R]
      extends RoutedPlus[EnvHttp[R, *]] with ConvertService[EnvHttp[R, *]] with LiftHttp[EnvHttp[R, *], Env[R, *]] {
    private type F[a] = EnvHttp[R, a]
    implicit private[this] val self: RoutedPlus[F] = this

    def matched: F[Int] = Env.fromFunc(_.matched)

    def withMatched[A](m: Int, fa: F[A]): F[A] = fa.local(_.copy(matched = m))

    def path: F[CharSequence]                 = Env.fromFunc(_.path)
    def request: F[http.Request]              = Env.fromFunc(_.request)
    def reject[A](rejection: Rejection): F[A] =
      Routed.unmatchedPath[F].flatMap(path => throwRej(rejection withPath path.toString))

    def combineK[A](x: F[A], y: F[A]): F[A] =
      catchRej(x)(xrs => catchRej(y)(yrs => throwRej(xrs |+| yrs)))

    def convertService[A](svc: Service[http.Request, A]): F[A] =
      Env { r =>
        Task.cancelable { cb =>
          val fut = svc(r.request).respond {
            case twitter.util.Return(a) => cb.onSuccess(a)
            case twitter.util.Throw(ex) => cb.onError(ex)
          }

          Task(fut.raise(new InterruptedException))
        }
      }

    def apply[A](fa: Env[R, A]): EnvHttp[R, A]                                 = fa.localP(_.embedded)
    @inline private[this] def catchRej[A](z: F[A])(f: Rejection => F[A]): F[A] =
      z.onErrorRecoverWith { case Rejected(xrs) => f(xrs) }

    @inline private[this] def throwRej[A](map: Rejection): F[A]                = Env.raiseError(envRouting.Rejected(map))
  }

  protected object envRoutedAny extends EnvRoutedConvert[Any]
} 
Example 122
Source File: TaskRouting.scala    From typed-schema   with Apache License 2.0 5 votes vote down vote up
package ru.tinkoff.tschema.finagle.envRouting

import cats.syntax.semigroup._
import com.twitter
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.finagle.{Service, http}
import com.twitter.util.{Future, Promise}
import monix.eval.Task
import monix.execution.Scheduler
import ru.tinkoff.tschema.finagle.Rejection.Recover
import ru.tinkoff.tschema.finagle._
import ru.tinkoff.tschema.finagle.envRouting.TaskRouting.TaskHttp
import ru.tinkoff.tschema.utils.SubString
import tofu.env.Env

final case class TaskRouting(
    request: http.Request,
    path: CharSequence,
    matched: Int
)

object TaskRouting extends TaskInstanceDecl {

  type TaskHttp[+A] = Env[TaskRouting, A]

  implicit val taskRouted: RoutedPlus[TaskHttp] with ConvertService[TaskHttp] with LiftHttp[TaskHttp, Task] =
    new TaskRoutedConvert

  implicit def envRunnable(implicit
      recover: Recover[TaskHttp] = Recover.default[TaskHttp]
  ): RunHttp[TaskHttp, Task] =
    response => Task.deferAction(implicit sched => Task.delay(execResponse(response, _)))

  private[this] def execResponse(
      envResponse: TaskHttp[Response],
      request: Request
  )(implicit sc: Scheduler, recover: Recover[TaskHttp]): Future[Response] = {
    val promise = Promise[Response]
    val routing = TaskRouting(request, SubString(request.path), 0)

    val cancelable = envResponse.onErrorRecoverWith { case Rejected(rej) => recover(rej) }.run(routing).runAsync {
      case Right(res) => promise.setValue(res)
      case Left(ex)   =>
        val resp = Response(Status.InternalServerError)
        resp.setContentString(ex.getMessage)
        promise.setValue(resp)
    }

    promise.setInterruptHandler { case _ => cancelable.cancel() }

    promise
  }
}

private[finagle] class TaskInstanceDecl {

  protected class TaskRoutedConvert
      extends RoutedPlus[TaskHttp] with ConvertService[TaskHttp] with LiftHttp[TaskHttp, Task] {
    private type F[a] = TaskHttp[a]
    implicit private[this] val self: RoutedPlus[F] = this

    def matched: F[Int] = Env.fromFunc(_.matched)

    def withMatched[A](m: Int, fa: F[A]): F[A] = fa.local(_.copy(matched = m))

    def path: F[CharSequence]                 = Env.fromFunc(_.path)
    def request: F[http.Request]              = Env.fromFunc(_.request)
    def reject[A](rejection: Rejection): F[A] =
      Routed.unmatchedPath[F].flatMap(path => throwRej(rejection withPath path.toString))

    def combineK[A](x: F[A], y: F[A]): F[A] =
      catchRej(x)(xrs => catchRej(y)(yrs => throwRej(xrs |+| yrs)))

    def convertService[A](svc: Service[http.Request, A]): F[A] =
      Env { r =>
        Task.cancelable { cb =>
          val fut = svc(r.request).respond {
            case twitter.util.Return(a) => cb.onSuccess(a)
            case twitter.util.Throw(ex) => cb.onError(ex)
          }

          Task(fut.raise(new InterruptedException))
        }
      }

    def apply[A](fa: Task[A]): TaskHttp[A] = Env.fromTask(fa)

    @inline private[this] def catchRej[A](z: F[A])(f: Rejection => F[A]): F[A] =
      z.onErrorRecoverWith { case Rejected(xrs) => f(xrs) }

    @inline private[this] def throwRej[A](map: Rejection): F[A]                = Env.raiseError(envRouting.Rejected(map))
  }

} 
Example 123
Source File: package.scala    From typed-schema   with Apache License 2.0 5 votes vote down vote up
package ru.tinkoff.tschema.finagle
import com.twitter.finagle.http.{Response, Status}
import com.twitter.util.{Future, Promise}
import zio.{Exit, Fiber, Has, Runtime, ZIO}

package object zioRouting {
  type NoError <: Nothing
  type None >: Any

  type ZRouting   = ZioRouting[Any]
  type HasRouting = Has[ZRouting]

  type UIOHttp[+A]         = ZIO[ZioRouting[None], Fail[NoError], A]
  type IOHttp[+E, +A]      = ZIO[ZioRouting[None], Fail[E], A]
  type TaskHttp[+A]        = ZIO[ZioRouting[None], Fail[Throwable], A]
  type URIOHttp[-R, +A]    = ZIO[ZioRouting[R], Fail[NoError], A]
  type RIOHttp[-R, +A]     = ZIO[ZioRouting[R], Fail[Throwable], A]
  type ZIOHttp[-R, +E, +A] = ZIO[ZioRouting[R], Fail[E], A]

  type UIOH[+A]         = ZIO[HasRouting, Fail[NoError], A]
  type IOH[+E, +A]      = ZIO[HasRouting, Fail[E], A]
  type TaskH[+A]        = ZIO[HasRouting, Fail[Throwable], A]
  type URIOH[-R, +A]    = ZIO[HasRouting with R, Fail[NoError], A]
  type RIOH[-R, +A]     = ZIO[HasRouting with R, Fail[Throwable], A]
  type ZIOH[-R, +E, +A] = ZIO[HasRouting with R, Fail[E], A]

  private[zioRouting] def execWithRuntime[R, E <: Throwable](runtime: Runtime[R])(
      zio: ZIO[R, E, Response]
  ): Future[Response] = {
    val promise = Promise[Response]

    runtime.unsafeRunAsync(setInterruption(zio, promise, runtime)) {
      case Exit.Success(resp)  => promise.setValue(resp)
      case Exit.Failure(cause) =>
        val resp  = Response(Status.InternalServerError)
        val error = cause.squash
        resp.setContentString(Option(error.getLocalizedMessage).getOrElse(error.toString))
        promise.setValue(resp)
    }

    promise
  }

  private def setInterruption[R, E, A, X](zio: ZIO[R, E, A], promise: Promise[X], rt: Runtime[Any]): ZIO[R, E, A] = {
    def setInterrupt(fiber: Fiber[Any, Any]) =
      ZIO.effectTotal(promise.setInterruptHandler {
        case _ => rt.unsafeRunAsync(fiber.interrupt)(_ => ())
      })

    zio.fork.tap(setInterrupt) >>= (_.join)
  }

  private[zioRouting] def execResponse[R, R1, E <: Throwable](
      runtime: zio.Runtime[R],
      zioResponse: ZIO[R1, E, Response],
      f: R => R1
  ): Future[Response] =
    zioRouting.execWithRuntime(runtime)(
      zioResponse.provideSome[R](f)
    )
} 
Example 124
Source File: Sessions.scala    From peregrine   with Apache License 2.0 5 votes vote down vote up
package io.peregrine

import com.twitter.finagle.http.Cookie
import com.twitter.util.Future

import scala.collection.mutable

trait Sessions extends BaseSessions {

  // needs to be overridden in order to create a different session type
  protected def createSession[S >: Session](sessionId: String) = new CookieBasedSession(sessionId)

  // needs to be overridden in order to create a different session type
  protected def getOrCreateSession(sessionId: String): Session = {
    CookieSessionsHolder.getOrElseUpdate(sessionId, createSession(sessionId))
  }
}

trait BaseSessions {

  protected def createSessionCookie: Cookie = {
    buildSessionCookie(IdGenerator.hexString(64))
  }

  protected def cookieBuilder: CookieBuilder = {
    new CookieBuilder
  }

  protected def buildSessionCookie(value: String): Cookie = {
    cookieBuilder.name("_session_id")
      .value(value)
      .httpOnly(httpOnly = true)
      // enables cookies for secure session if cert and key are provided
      .secure(!config.certificatePath().isEmpty && !config.keyPath().isEmpty)
      .build()
  }

  // needs to be overridden in order to create a different session type
  def session(implicit req: Request): Future[Session] = Future {
    req.cookies.get("_session_id") match {
      case Some(cookie) =>
        req.response.addCookie(buildSessionCookie(cookie.value))
        getOrCreateSession(cookie.value)
      case None         =>
        val cookie = createSessionCookie
        req.response.addCookie(cookie)
        getOrCreateSession(cookie.value)
    }
  }

  // needs to be overridden in order to create a different session type
  protected def createSession[S >: Session](sessionId: String): S

  // needs to be overridden in order to create a different session type
  protected def getOrCreateSession(sessionId: String): Session
}

trait Session {
  type Seconds = Long

  def get[T](key: String): Future[Option[T]]
  def put[T](key: String, value: T, expiresIn: Seconds = 3600000): Future[Unit]
  def del(key: String): Future[Unit]
  def getOrElseUpdate[T](key: String, value: T): Future[T]
}

class CookieBasedSession(val sessionId: String) extends Session {

  private val values = mutable.Map[String, Any]()

  override def get[T](key: String): Future[Option[T]] = Future(values.get(key).map(_.asInstanceOf[T]))
  override def put[T](key: String, value: T, expiresIn: Seconds = 3600000): Future[Unit] = Future(values.put(key, value))
  override def del(key: String): Future[Unit] = Future(values.remove(key))
  override def getOrElseUpdate[T](key: String, value: T):Future[T] = get[T](key).flatMap {
    case Some(t) => Future(t)
    case None    => put[T](key, value).map(_ => value)
  }
}

private[this] object CookieSessionsHolder {

  private val sessions = mutable.Map[String, CookieBasedSession]()

  def get(id: String): Option[Session] = sessions.get(id)
  def put(id: String, session: CookieBasedSession): Unit = sessions.put(id, session)
  def del(id: String): Unit = sessions.remove(id)
  def getOrElseUpdate(id: String, session: CookieBasedSession) = sessions.getOrElseUpdate(id, session)
}

private[peregrine] object IdGenerator {

  import java.security.SecureRandom

  private val secureRandom = new SecureRandom()

  def hexString(bytes: Int): String = {
    val data = new Array[Byte](bytes)
    secureRandom.nextBytes(data)
    data.map("%02x" format _).mkString
  }
} 
Example 125
Source File: AppService.scala    From peregrine   with Apache License 2.0 5 votes vote down vote up
package io.peregrine

import com.twitter.finagle.Service
import com.twitter.finagle.http.{Request => FinagleRequest, Response => FinagleResponse}
import com.twitter.util.{Await, Future}

class AppService(controllers: ControllerCollection)
  extends Service[FinagleRequest, FinagleResponse] {

  def render: ResponseBuilder = new ResponseBuilder

  def apply(rawRequest: FinagleRequest): Future[FinagleResponse] = {
    val adaptedRequest = RequestAdapter(rawRequest)

    try {
      attemptRequest(rawRequest).handle {
        case t: Throwable =>
          Await.result(
            ErrorHandler(adaptedRequest, t, controllers)
          )
      }
    } catch {
      case e: Exception =>
        ErrorHandler(adaptedRequest, e, controllers)
    }
  }

  def attemptRequest(rawRequest: FinagleRequest): Future[FinagleResponse] = {
    val adaptedRequest = RequestAdapter(rawRequest)

    controllers.dispatch(rawRequest) match {
      case Some(response) =>
        response.asInstanceOf[Future[FinagleResponse]]
      case None           =>
        ResponseAdapter(adaptedRequest, controllers.notFoundHandler(adaptedRequest))
    }
  }
} 
Example 126
Source File: CsrfFilter.scala    From peregrine   with Apache License 2.0 5 votes vote down vote up
package io.peregrine

import com.twitter.finagle.http.{Request => FinagleRequest, Response => FinagleResponse}
import com.twitter.finagle.{Service, SimpleFilter}
import com.twitter.util.{Future,Await}
import org.jboss.netty.handler.codec.http.HttpMethod

class CsrfFilter extends SimpleFilter[FinagleRequest, FinagleResponse] with Sessions {

  def apply(req: FinagleRequest, service: Service[FinagleRequest, FinagleResponse]): Future[FinagleResponse] = {
    // we bypass on assets requests
    if (req.path.startsWith(config.assetsPathPrefix())) {
      service(req)
    } else {
      _applyCsrfProtection(req, service)
    }
  }

  private def _applyCsrfProtection(req: FinagleRequest, service: Service[FinagleRequest, FinagleResponse]) = {
    if (req.method == HttpMethod.GET) {
      for {
        _           <- _addCsrfToken(req)
        res         <- service(req)
      } yield res
    } else {
      for {
        csrfToken   <- _addCsrfToken(req)
        authToken   <- Future(req.cookies.getOrElse("_authenticity_token", buildVoidCookie).value)
        paramToken  <- Future(req.params.getOrElse("_csrf_token", ""))
        res         <- if (csrfToken == paramToken && csrfToken == authToken) service(req)
                       else Future(new ResponseBuilder().status(403).body("CSRF failed").build)
      } yield res
    }
  }

  private def _addCsrfToken(req: FinagleRequest) = {
    for {
      session     <- session(new Request(req))
      csrfToken   <- session.getOrElseUpdate[String]("_csrf_token", generateToken)
      _           <- Future(req.response.addCookie(buildCsrfCookie(csrfToken)))
    } yield csrfToken
  }

  protected def generateToken = IdGenerator.hexString(32)

  private def buildVoidCookie = new CookieBuilder().name("_authenticity_token").value("").build()
  private def buildCsrfCookie(value: String) = {
    new CookieBuilder().name("_authenticity_token")
      .value(value)
      .httpOnly(httpOnly = true)
      // enables cookies for secure session if cert and key are provided
      .secure(!config.certificatePath().isEmpty && !config.keyPath().isEmpty)
      .build()
  }
} 
Example 127
Source File: SpecHelper.scala    From peregrine   with Apache License 2.0 5 votes vote down vote up
package io.peregrine

import com.twitter.finagle.http.{Request => FinagleRequest, Response => FinagleResponse}
import com.twitter.util.{Await, Future}
import org.jboss.netty.handler.codec.http.HttpMethod
import org.jboss.netty.util.CharsetUtil.UTF_8

import scala.collection.Map

class MockResponse(val originalResponse: FinagleResponse) {

  def status                  = originalResponse.getStatus()
  def code                    = originalResponse.getStatus().getCode
  def body                    = originalResponse.getContent().toString(UTF_8)
  def getHeader(name: String) = originalResponse.headers().get(name)
  def getHeaders              = originalResponse.headerMap

}

trait SpecHelper {

  def response  = new MockResponse(Await.result(lastResponse))
  var lastResponse: Future[FinagleResponse] = null

  def server: PeregrineServer

  def get(path:String, params:Map[String,String]=Map(), headers:Map[String,String]=Map()) {
    executeRequest(HttpMethod.GET,path,params,headers)
  }

  def post(path:String, params:Map[String,String]=Map(), headers:Map[String,String]=Map(), body:AnyRef=null) {
    executeRequest(HttpMethod.POST,path,params,headers,body)
  }

  def put(path:String, params:Map[String,String]=Map(), headers:Map[String,String]=Map(), body:AnyRef=null) {
    executeRequest(HttpMethod.PUT,path,params,headers,body)
  }

  def delete(path:String, params:Map[String,String]=Map(), headers:Map[String,String]=Map()) {
    executeRequest(HttpMethod.DELETE,path,params,headers)
  }

  def head(path:String,params:Map[String,String]=Map(), headers:Map[String,String]=Map()) {
    executeRequest(HttpMethod.HEAD,path,params,headers)
  }

  def patch(path:String, params:Map[String,String]=Map(), headers:Map[String,String]=Map()) {
    executeRequest(HttpMethod.PATCH,path,params,headers)
  }

  def options(path:String, params:Map[String,String]=Map(), headers:Map[String,String]=Map(), body:AnyRef=null) {
    executeRequest(HttpMethod.OPTIONS,path,params,headers,body)
  }

  def send(request: FinagleRequest) {
    executeRequest(request)
  }

  private def executeRequest(
                              method: HttpMethod,
                              path: String,
                              params: Map[String, String] = Map(),
                              headers: Map[String,String] = Map(),
                              body: AnyRef = null
                              ) {
    val app = MockApp(server)
    val result: MockResult = app.execute(method = method, path = path, params = params, headers = headers, body = body)
    lastResponse = result.response
  }

  private def executeRequest(request: FinagleRequest) {
    val app = MockApp(server)
    val result: MockResult = app.execute(request)
    lastResponse = result.response
  }

} 
Example 128
Source File: ControllerCollection.scala    From peregrine   with Apache License 2.0 5 votes vote down vote up
package io.peregrine

import com.twitter.finagle.http.{Request => FinagleRequest, Response => FinagleResponse}
import com.twitter.util.Future

class ControllerCollection {
  var controllers: Seq[Controller] = Seq.empty

  var notFoundHandler = { request: Request =>
    render.status(404).plain("Not Found").toFuture
  }

  var errorHandler = { request: Request =>
    request.error match {
      case Some(e: UnsupportedMediaType) =>
        render.status(415).plain("No handler for this media type found").toFuture
      case _                             =>
        render.status(500).plain("Something went wrong!").toFuture
    }
  }

  def render: ResponseBuilder = new ResponseBuilder

  def dispatch(request: FinagleRequest): Option[Future[FinagleResponse]] = {
    var response: Option[Future[FinagleResponse]] = None

    controllers.find { ctrl =>
      ctrl.route.dispatch(request) match {
        case Some(callbackResponse) =>
          response = Some(callbackResponse)
          true
        case None                   =>
          false
      }
    }

    response
  }

  def add(controller: Controller) {
    notFoundHandler = controller.notFoundHandler.getOrElse(notFoundHandler)
    errorHandler = controller.errorHandler.getOrElse(errorHandler)
    controllers = controllers ++ Seq(controller)
  }
} 
Example 129
Source File: RouteVector.scala    From peregrine   with Apache License 2.0 5 votes vote down vote up
package io.peregrine

import com.twitter.util.Future
import org.jboss.netty.handler.codec.http._

case class Route(method: HttpMethod, path: String, pattern: PathPattern, callback: Request => Future[ResponseBuilder])

class RouteVector {
  var vector = Vector[Route]()

  def add(x: Route) {
    vector = x +: vector
  }

  def withPrefix(prefix: String) = {
    vector = vector.map { r =>
      val composedPath = prefix + r.path
      r.copy(
        path    = composedPath,
        pattern = SinatraPathPatternParser(composedPath)
      )
    }
  }
} 
Example 130
Source File: ErrorHandlerSpec.scala    From peregrine   with Apache License 2.0 5 votes vote down vote up
package io.peregrine

import io.peregrine.test.FlatSpecHelper
import com.twitter.util.Future

class ErrorHandlerSpec extends FlatSpecHelper {

  case class TheException() extends Exception

  class HandlingCtrl extends Controller {
    get("/handled") { request =>
      Future.exception(TheException())
    }

    error { request =>
      request.error match {
        case Some(TheException()) => render.ok.toFuture
        case _ => render.internalServerError.toFuture
      }
    }
  }

  class FailingCtrl extends Controller {
    get("/unhandled") { request =>
      Future.exception(TheException())
    }

    // We still need to specify an error handler, otherwise, we fallback on the other controller's handler
    // Fixing that requires changes to the API.
    error { request =>
      request.error match {
        case _ => render.internalServerError.toFuture
      }
    }
  }

  val server = new PeregrineServer
  server.register(new HandlingCtrl())
  server.register(new FailingCtrl())

  "ErrorHandler" should "handle exceptions" in {
    get("/handled")
    response.code should equal (200)
    get("/unhandled")
    response.code should equal (500)
  }
} 
Example 131
Source File: SwaggerFinatra.scala    From tapir   with Apache License 2.0 5 votes vote down vote up
package sttp.tapir.swagger.finatra

import java.io.InputStream
import java.util.Properties

import com.twitter.finagle.http.Request
import com.twitter.finatra.http.Controller
import com.twitter.io.{Buf, InputStreamReader, Reader}
import com.twitter.util.Future


class SwaggerFinatra(yaml: String, contextPath: String = "docs", yamlName: String = "docs.yaml") extends Controller {
  private val swaggerVersion: String = {
    val p = new Properties()
    val pomProperties = getClass.getResourceAsStream("/META-INF/maven/org.webjars/swagger-ui/pom.properties")
    try p.load(pomProperties)
    finally pomProperties.close()
    p.getProperty("version")
  }

  get(route = s"/$contextPath/?") { _: Request =>
    response.movedPermanently.location(s"/$contextPath/index.html?url=/$contextPath/$yamlName")
  }

  get(route = s"/$contextPath/$yamlName") { _: Request => response.ok(yaml).contentType("application/yaml") }

  get(route = s"/$contextPath/:swaggerResource") { req: Request =>
    val sResource: String = req.getParam("swaggerResource")
    val sIStreamOpt: Option[InputStream] =
      Option(getClass.getResourceAsStream(s"/META-INF/resources/webjars/swagger-ui/$swaggerVersion/$sResource"))

    sIStreamOpt.fold(Future.value(response.notFound))(is =>
      Reader
        .readAllItems(
          InputStreamReader(is)
        )
        .map { bs =>
          val bytes: Array[Byte] = Buf.ByteArray.Shared.extract(bs.fold(Buf.Empty)(_.concat(_)))

          if (sResource.endsWith(".html")) {
            response.ok.html(new String(bytes, "UTF-8"))
          } else if (sResource.endsWith(".css")) {
            response.ok(new String(bytes, "UTF-8")).contentType("text/css")
          } else if (sResource.endsWith(".js")) {
            response.ok(new String(bytes, "UTF-8")).contentType("text/javascript")
          } else {
            response.ok(bytes).contentType("image/png")
          }
        }
    )
  }
} 
Example 132
Source File: FinatraServerOptions.scala    From tapir   with Apache License 2.0 5 votes vote down vote up
package sttp.tapir.server.finatra

import java.io.{File, FileOutputStream}

import com.twitter.util.logging.Logging
import com.twitter.util.{Future, FuturePool}
import sttp.tapir.Defaults
import sttp.tapir.server.{DecodeFailureHandler, LogRequestHandling, ServerDefaults}

case class FinatraServerOptions(
    createFile: Array[Byte] => Future[File],
    decodeFailureHandler: DecodeFailureHandler,
    logRequestHandling: LogRequestHandling[Unit]
)

object FinatraServerOptions extends Logging {
  implicit lazy val default: FinatraServerOptions = FinatraServerOptions(
    defaultCreateFile(futurePool),
    ServerDefaults.decodeFailureHandler,
    defaultLogRequestHandling
  )

  def defaultCreateFile(futurePool: FuturePool)(bytes: Array[Byte]): Future[File] = {
    // TODO: Make this streaming
    futurePool {
      val file = Defaults.createTempFile()
      val outputStream = new FileOutputStream(file)
      outputStream.write(bytes)
      outputStream.close()
      file
    }
  }

  private lazy val futurePool = FuturePool.unboundedPool

  lazy val defaultLogRequestHandling: LogRequestHandling[Unit] = LogRequestHandling(
    doLogWhenHandled = debugLog,
    doLogAllDecodeFailures = debugLog,
    doLogLogicExceptions = (msg: String, ex: Throwable) => error(msg, ex),
    noLog = ()
  )

  private def debugLog(msg: String, exOpt: Option[Throwable]): Unit =
    exOpt match {
      case None     => debug(msg)
      case Some(ex) => debug(msg, ex)
    }
} 
Example 133
Source File: FinatraRequestToRawBody.scala    From tapir   with Apache License 2.0 5 votes vote down vote up
package sttp.tapir.server.finatra

import java.io.ByteArrayInputStream
import java.nio.ByteBuffer
import java.nio.charset.Charset

import com.twitter.finagle.http.Request
import com.twitter.finatra.http.request.RequestUtils
import com.twitter.io.Buf
import com.twitter.util.Future
import org.apache.commons.fileupload.FileItemHeaders
import sttp.model.{Part, Header}
import sttp.tapir.{RawPart, RawBodyType}

import scala.collection.immutable.Seq
import scala.collection.JavaConverters._

class FinatraRequestToRawBody(serverOptions: FinatraServerOptions) {
  def apply[R](bodyType: RawBodyType[R], body: Buf, charset: Option[Charset], request: Request): Future[R] = {
    def asByteArray: Array[Byte] = {
      val array = new Array[Byte](body.length)
      body.write(array, 0)
      array
    }

    def asByteBuffer: ByteBuffer = {
      val buffer = ByteBuffer.allocate(body.length)
      body.write(buffer)
      buffer.flip()
      buffer
    }

    bodyType match {
      case RawBodyType.StringBody(defaultCharset) => Future.value[R](new String(asByteArray, charset.getOrElse(defaultCharset)))
      case RawBodyType.ByteArrayBody              => Future.value[R](asByteArray)
      case RawBodyType.ByteBufferBody             => Future.value[R](asByteBuffer)
      case RawBodyType.InputStreamBody            => Future.value[R](new ByteArrayInputStream(asByteArray))
      case RawBodyType.FileBody                   => serverOptions.createFile(asByteArray)
      case m: RawBodyType.MultipartBody           => multiPartRequestToRawBody(request, m)
    }
  }

  private def parseDispositionParams(headerValue: Option[String]): Map[String, String] =
    headerValue
      .map(
        _.split(";")
          .map(_.trim)
          .tail
          .map(_.split("="))
          .map(array => array(0) -> array(1))
          .toMap
      )
      .getOrElse(Map.empty)

  private def getCharset(contentType: Option[String]): Option[Charset] =
    contentType.flatMap(
      _.split(";")
        .map(_.trim)
        .tail
        .map(_.split("="))
        .map(array => array(0) -> array(1))
        .toMap
        .get("charset")
        .map(Charset.forName)
    )

  private def multiPartRequestToRawBody(request: Request, m: RawBodyType.MultipartBody): Future[Seq[RawPart]] = {
    def fileItemHeaders(headers: FileItemHeaders): Seq[Header] = {
      headers.getHeaderNames.asScala
        .flatMap { name => headers.getHeaders(name).asScala.map(name -> _) }
        .toSeq
        .filter(_._1.toLowerCase != "content-disposition")
        .map { case (k, v) => Header(k, v) }
        .toList
    }

    Future
      .collect(
        RequestUtils
          .multiParams(request)
          .flatMap {
            case (name, multiPartItem) =>
              val dispositionParams: Map[String, String] =
                parseDispositionParams(Option(multiPartItem.headers.getHeader("content-disposition")))
              val charset = getCharset(multiPartItem.contentType)

              for {
                partType <- m.partType(name)
                futureBody = apply(partType, Buf.ByteArray.Owned(multiPartItem.data), charset, request)
              } yield futureBody
                .map(body =>
                  Part(name, body, otherDispositionParams = dispositionParams - "name", headers = fileItemHeaders(multiPartItem.headers))
                    .asInstanceOf[RawPart]
                )
          }
          .toSeq
      )
      .map(_.toList)
  }
} 
Example 134
Source File: FinatraServerTests.scala    From tapir   with Apache License 2.0 5 votes vote down vote up
package sttp.tapir.server.finatra

import cats.data.NonEmptyList
import cats.effect.{ContextShift, IO, Resource, Timer}
import com.github.ghik.silencer.silent
import com.twitter.finagle.http.Request
import com.twitter.finatra.http.filters.{AccessLoggingFilter, ExceptionMappingFilter}
import com.twitter.finatra.http.{Controller, EmbeddedHttpServer, HttpServer}
import com.twitter.finatra.http.routing.HttpRouter
import com.twitter.util.{Future, FuturePool}
import sttp.tapir.Endpoint
import sttp.tapir.server.{DecodeFailureHandler, ServerDefaults, ServerEndpoint}
import sttp.tapir.server.tests.ServerTests
import sttp.tapir.tests.{Port, PortCounter}

import scala.concurrent.ExecutionContext
import scala.reflect.ClassTag
import scala.concurrent.duration._

class FinatraServerTests extends ServerTests[Future, Nothing, FinatraRoute] {
  override def streamingSupport: Boolean = false

  private val futurePool = FuturePool.unboundedPool

  implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global
  implicit val contextShift: ContextShift[IO] = IO.contextShift(ec)
  implicit val timer: Timer[IO] = IO.timer(ec)

  override def pureResult[T](t: T): Future[T] = Future.value(t)

  override def suspendResult[T](t: => T): Future[T] =
    futurePool {
      t
    }

  override def route[I, E, O](
      e: ServerEndpoint[I, E, O, Nothing, Future],
      decodeFailureHandler: Option[DecodeFailureHandler] = None
  ): FinatraRoute = {
    implicit val serverOptions: FinatraServerOptions =
      FinatraServerOptions.default.copy(decodeFailureHandler = decodeFailureHandler.getOrElse(ServerDefaults.decodeFailureHandler))
    e.toRoute
  }

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

  override def server(routes: NonEmptyList[FinatraRoute], port: Port): Resource[IO, Unit] = FinatraServerTests.server(routes, port)

  override lazy val portCounter: PortCounter = new PortCounter(58000)
}

object FinatraServerTests {
  def server(routes: NonEmptyList[FinatraRoute], port: Port)(implicit ioTimer: Timer[IO]): Resource[IO, Unit] = {
    def waitUntilHealthy(s: EmbeddedHttpServer, count: Int): IO[EmbeddedHttpServer] =
      if (s.isHealthy) IO.pure(s)
      else if (count > 1000) IO.raiseError(new IllegalStateException("Server unhealthy"))
      else IO.sleep(10.milliseconds).flatMap(_ => waitUntilHealthy(s, count + 1))

    val bind = IO {
      class TestController extends Controller with TapirController {
        routes.toList.foreach(addTapirRoute)
      }

      class TestServer extends HttpServer {
        @silent("discarded")
        override protected def configureHttp(router: HttpRouter): Unit = {
          router
            .filter[AccessLoggingFilter[Request]]
            .filter[ExceptionMappingFilter[Request]]
            .add(new TestController)
        }
      }

      val server = new EmbeddedHttpServer(
        new TestServer,
        Map(
          "http.port" -> s":$port"
        ),
        // in the default implementation waitForWarmup suspends the thread for 1 second between healthy checks
        // we improve on that by checking every 10ms
        waitForWarmup = false
      )
      server.start()
      server
    }.flatMap(waitUntilHealthy(_, 0))

    Resource
      .make(bind)(httpServer => IO(httpServer.close()))
      .map(_ => ())
  }
}