com.twitter.finagle.http.Request Scala Examples

The following examples show how to use com.twitter.finagle.http.Request. 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: Http_03_Client.scala    From airframe   with Apache License 2.0 5 votes vote down vote up
package wvlet.airframe.examples.http

import java.util.concurrent.TimeUnit

import com.twitter.finagle.Http
import com.twitter.finagle.http.Request
import com.twitter.util.Duration
import wvlet.airframe.http.finagle.{Finagle, FinagleSyncClient}
import wvlet.airframe.http.{Endpoint, HttpMethod, Router}
import wvlet.log.LogSupport


object Http_03_Client extends App with LogSupport {
  case class User(id: String, name: String)
  trait MyApp extends LogSupport {
    @Endpoint(method = HttpMethod.GET, path = "/user/:id")
    def getUser(id: String): User = {
      User(id, "xxx")
    }
  }

  val router = Router.add[MyApp]

  val serverDesign = Finagle.server
    .withName("myapp")
    .withRouter(router)
    .design

  val clietnDesign =
    Finagle.client
    // Max retry attempts
      .withMaxRetry(3)
      // Use backoff (or jittering)
      .withBackOff(1)
      // Set request timeout
      .withTimeout(Duration(90, TimeUnit.SECONDS))
      // Add Finagle specific configuration
      .withInitializer { client: Http.Client => client.withHttp2 } // optional
      // Create a new http client to access the server.
      .syncClientDesign

  val design = serverDesign + clietnDesign

  design.build[FinagleSyncClient] { client =>
    // FinagleServer will be started here
    // Read the JSON response as an object
    val user = client.get[User]("/user/1")
    debug(user)

    // Read the response as is
    val request = client.send(Request("/user/2"))
    val content = request.contentString
    debug(content)
  }
} 
Example 3
Source File: HttpRequestMatcher.scala    From airframe   with Apache License 2.0 5 votes vote down vote up
package wvlet.airframe.http.recorder

import java.util.Locale

import com.twitter.finagle.http.{MediaType, Request}
import wvlet.log.LogSupport


trait HttpRequestMatcher {
  def computeHash(request: Request): Int
}

object HttpRequestMatcher {
  // Http headers to ignore for request hashing purposes
  def defaultExcludeHeaderPrefixes: Seq[String] =
    Seq(
      "date",           // unstable header
      "x-b3-",          // Finagle's tracing IDs
      "finagle-",       // Finagle specific headers
      "host",           // The host value can be changed
      "content-length", // this can be 0 (or missing)
      "connection",     // Client might set this header
      "user-agent"      // User-agent can be arbitrary
    )

  def newRequestMatcher(extraHeadersToExclude: Seq[String]): HttpRequestMatcher = {
    new DefaultHttpRequestMatcher(defaultExcludeHeaderPrefixes ++ extraHeadersToExclude)
  }

  class DefaultHttpRequestMatcher(excludeHeaderPrefixes: Seq[String] = defaultExcludeHeaderPrefixes)
      extends HttpRequestMatcher
      with LogSupport {
    private val excludeHeaderPrefixesLowerCase: Seq[String] = excludeHeaderPrefixes.map(_.toLowerCase(Locale.ENGLISH))

    def computeHash(request: Request): Int = {
      val prefix             = computeRequestPathHash(request)
      val httpHeadersForHash = filterHeaders(request, excludeHeaderPrefixesLowerCase)
      trace(s"http headers for request ${request}: ${httpHeadersForHash.mkString(",")}")

      httpHeadersForHash match {
        case headers if headers.isEmpty => prefix.hashCode * 13
        case headers =>
          val headerHash = headers
            .map { x => s"${x._1.toLowerCase(Locale.ENGLISH)}:${x._2}".hashCode }.reduce { (xor, next) =>
              xor ^ next // Take XOR to compute order-insensitive hash values.
            }
          prefix.hashCode * 13 + headerHash
      }
    }
  }

  private def computeRequestPathHash(request: Request): Int = {
    val contentHash = request.content.hashCode()
    s"${request.method.toString()}:${request.uri}:${contentHash}".hashCode
  }

  def filterHeaders(request: Request, excludePrefixes: Seq[String]): Map[String, String] = {
    request.headerMap.toSeq.filterNot { x =>
      val key = x._1.toLowerCase(Locale.ENGLISH)
      excludePrefixes.exists(ex => key.startsWith(ex))
    }.toMap
  }

  object PathOnlyMatcher extends HttpRequestMatcher {
    override def computeHash(request: Request): Int = {
      computeRequestPathHash(request)
    }
  }
} 
Example 4
Source File: PathOnlyMatcherTest.scala    From airframe   with Apache License 2.0 5 votes vote down vote up
package wvlet.airframe.http.recorder

import com.twitter.finagle.http.{Request, Response}
import wvlet.airframe.Design
import wvlet.airframe.http.finagle.{FinagleClient, FinagleSyncClient}
import wvlet.airspec.AirSpec


class PathOnlyMatcherTest extends AirSpec {
  protected override def design: Design = {
    val config = HttpRecorderConfig(requestMatcher = HttpRequestMatcher.PathOnlyMatcher)
    Design.newDesign
      .bind[HttpRecorderServer].toInstance(HttpRecorder.createInMemoryServer(config))
      .onStart { recorder => // Record responses
        {
          val req = Request("/hello")
          req.headerMap.put("Accept-Encoding", "gzip")
          val resp = Response()
          resp.contentString = "hello"
          recorder.recordIfNotExists(req, resp)
        }

        {
          val r = Request("/hello-hello")
          r.headerMap.put("Accept-Encoding", "gzip")
          val resp = Response()
          resp.contentString = "hello-hello"
          recorder.recordIfNotExists(r, resp)
        }
      }
      .bind[FinagleSyncClient].toProvider { recorder: HttpRecorderServer =>
        FinagleClient.newSyncClient(recorder.localAddress)
      }
  }

  def `support simple path matcher`(client: FinagleSyncClient): Unit = {
    client.get[String]("/hello") shouldBe "hello"
    client.get[String]("/hello-hello") shouldBe "hello-hello"
  }
} 
Example 5
Source File: FinagleRetryFilter.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, Status}
import com.twitter.finagle.util.DefaultTimer
import com.twitter.finagle.{Service, SimpleFilter, http}
import com.twitter.util._
import wvlet.airframe.control.ResultClass
import wvlet.airframe.control.Retry.{MaxRetryException, RetryContext}
import wvlet.airframe.http.{HttpClientException, HttpClientMaxRetryException}
import wvlet.log.LogSupport


class FinagleRetryFilter(retry: RetryContext, timer: Timer = DefaultTimer)
    extends SimpleFilter[http.Request, http.Response]
    with LogSupport {
  import com.twitter.conversions.DurationOps._

  private[this] def schedule(d: Duration)(f: => Future[Response]) = {
    if (d > 0.seconds) {
      val promise = new Promise[Response]
      timer.schedule(Time.now + d) {
        promise.become(f)
      }
      promise
    } else {
      f
    }
  }

  private def dispatch(
      retryContext: RetryContext,
      request: Request,
      service: Service[Request, Response]
  ): Future[Response] = {
    val rep = service(request)
    rep.transform { x =>
      val classifier = x match {
        case Throw(e) =>
          retryContext.errorClassifier(e)
        case Return(r) =>
          retryContext.resultClassifier(r)
      }

      classifier match {
        case ResultClass.Succeeded =>
          rep
        case ResultClass.Failed(isRetryable, cause, extraWait) => {
          if (!retryContext.canContinue) {
            // Reached the max retry
            rep.flatMap { r =>
              Future.exception(HttpClientMaxRetryException(FinagleHttpResponseWrapper(r), retryContext, cause))
            }
          } else if (!isRetryable) {
            // Non-retryable failure
            Future.exception(cause)
          } else {
            Future
              .value {
                // Update the retry count
                retryContext.withExtraWait(extraWait).nextRetry(cause)
              }.flatMap { nextRetryContext =>
                // Wait until the next retry
                schedule(nextRetryContext.nextWaitMillis.millis) {
                  // Run the same request again
                  dispatch(nextRetryContext, request, service)
                }
              }
          }
        }
      }
    }
  }

  override def apply(request: Request, service: Service[Request, Response]): Future[Response] = {
    val retryContext = retry.init(Option(request))
    dispatch(retryContext, request, service)
  }
} 
Example 6
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 7
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 8
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 9
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 10
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 11
Source File: CustomCodecTest.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
import wvlet.airframe.codec.{MessageCodec, MessageContext}
import wvlet.airframe.control.Control
import wvlet.airframe.http.{Endpoint, RPC, Router}
import wvlet.airframe.msgpack.spi.{Packer, Unpacker}
import wvlet.airframe.surface.Surface
import wvlet.airspec.AirSpec
import wvlet.log.LogSupport


object CustomCodecTest extends AirSpec {

  sealed trait Suit
  case object Spade extends Suit
  case object Heart extends Suit

  object SuitCodec extends MessageCodec[Suit] {
    override def pack(
        p: Packer,
        v: Suit
    ): Unit = {
      p.packString(v.toString)
    }
    override def unpack(
        u: Unpacker,
        v: MessageContext
    ): Unit = {
      u.unpackString match {
        case "Spade" => v.setObject(Spade)
        case "Heart" => v.setObject(Heart)
        case other   => v.setError(new IllegalArgumentException(s"Unknown suit: ${other}"))
      }
    }
  }

  class MyApi extends LogSupport {
    @Endpoint(path = "/hello")
    def hello(suit: Suit): String = {
      suit.toString
    }
  }

  test(
    s"custom codec",
    design = Finagle.server
      .withRouter(Router.add[MyApi])
      .withCustomCodec(Map(Surface.of[Suit] -> SuitCodec))
      .design
      .add(Finagle.client.syncClientDesign)
  ) { client: FinagleSyncClient =>
    client.send(Request("/hello?suit=Spade")).contentString shouldBe "Spade"
    client.send(Request("/hello?suit=Heart")).contentString shouldBe "Heart"
  }

} 
Example 12
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 13
Source File: ErrorResponseTest.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
import wvlet.airframe.Design
import wvlet.airframe.codec.MessageCodec
import wvlet.airframe.http.{Endpoint, Http, HttpHeader, HttpMessage, HttpStatus, RPC, Router}
import wvlet.airspec.AirSpec
import wvlet.log.LogSupport


object ErrorResponseTest extends AirSpec {

  case class ErrorResponse(code: Int, message: String)

  trait MyApp extends LogSupport {
    @Endpoint(path = "/v1/test")
    def errorResponse(req: HttpMessage.Request): String = {
      throw Http.serverException(req, HttpStatus.BadRequest_400, ErrorResponse(10, "error test"))
    }
  }

  test(
    "Return error response using JSON/MsgPack",
    design = Finagle.server.withRouter(Router.of[MyApp]).design + Finagle.client.syncClientDesign
  ) { client: FinagleSyncClient =>
    warn(s"Running an error response test")

    test("json error response") {
      val resp = client.sendSafe(Request("/v1/test"))
      resp.statusCode shouldBe 400
      resp.contentString shouldBe """{"code":10,"message":"error test"}"""
    }

    test("msgpack error response") {
      val req = Request("/v1/test")
      req.accept = HttpHeader.MediaType.ApplicationMsgPack
      val resp = client.sendSafe(req)
      resp.statusCode shouldBe 400
      val msgpack = resp.contentBytes
      val e       = MessageCodec.of[ErrorResponse].fromMsgPack(msgpack)
      e shouldBe ErrorResponse(10, "error test")
    }

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

import javax.inject.{Inject, Singleton}

import com.google.inject.{Module, Provides}
import com.twitter.finagle.Dtab
import com.twitter.finagle.http.{Request, Response}
import com.twitter.finatra.http.{Controller, HttpServer}
import com.twitter.finatra.http.filters.{CommonFilters, LoggingMDCFilter, TraceIdMDCFilter}
import com.twitter.finatra.http.routing.HttpRouter
import com.twitter.finatra.response.Mustache
import com.twitter.inject.TwitterModule
import com.twitter.inject.annotations.Flag
import net.gutefrage.Dtabs
import net.gutefrage.temperature.thrift.TemperatureService

object FinatraHttpServer extends HttpServer {

  premain {
    Dtabs.init()
  }

  override def modules: Seq[Module] = Seq(TemperatureServiceModule)

  override def defaultFinatraHttpPort = ":9000"

  override protected def configureHttp(router: HttpRouter): Unit =
    router
      .filter[LoggingMDCFilter[Request, Response]]
      .filter[TraceIdMDCFilter[Request, Response]]
      .filter[CommonFilters]
      .add[WeatherController]
}


class WeatherController @Inject()(
  temperatureService: TemperatureService.FutureIface,
  @Flag("local.doc.root") localDocRoot: String,
  @Flag("doc.root") docRoot: String
) extends Controller {

  private val docConfig = DocConfig(localDocRoot, docRoot)

  get("/") { request: Request =>
    temperatureService.mean().map { meanTemperature =>
      DashboardData(meanTemperature, Some("local"), docConfig)
    }
  }

  // Change dtab according to the environment
  get("/:env") { request: Request =>
    val environment = request.params("env")

    Dtab.unwind {
      Dtab.local = Dtab.read(s"/env => /s#/$environment")
      temperatureService.mean().map { meanTemperature =>
        DashboardData(meanTemperature, Some(environment), docConfig)
      }
    }
  }

  // Asset route (for styles.css)
  get("/assets/:*") { request: Request =>
    response.ok.file("/assets/" + request.params("*"))
  }
}

@Mustache("dashboard")
case class DashboardData(
  meanTemperature: Double,
  environment: Option[String],
  docConfig: DocConfig
)
case class DocConfig(localDocRoot: String, docRoot: String) 
Example 15
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 16
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 17
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 18
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 19
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 20
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 21
Source File: MetricsServiceSpec.scala    From finagle-prometheus   with MIT License 5 votes vote down vote up
package com.samstarling.prometheusfinagle.metrics

import com.samstarling.prometheusfinagle.UnitTest
import com.twitter.finagle.http.{Method, Request}
import com.twitter.util.Await
import io.prometheus.client.CollectorRegistry
import org.specs2.specification.Scope

class MetricsServiceSpec extends UnitTest {

  trait Context extends Scope {
    val registry = new CollectorRegistry(true)
    val telemetry = new Telemetry(registry, "unit_test")
    val service = new MetricsService(registry)
  }

  "it renders metrics correctly" in new Context {
    telemetry.counter("foo").inc()
    val request = Request(Method.Get, "/")
    val response = Await.result(service.apply(request))

    response.getContentString.trim ====
      "# HELP unit_test_foo No help provided\n" +
        "# TYPE unit_test_foo counter\n" +
        "unit_test_foo 1.0"
  }
} 
Example 22
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 23
Source File: HttpServiceLabellerSpec.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.twitter.finagle.http.{Method, Request, Response, Status}
import org.specs2.specification.Scope

class HttpServiceLabellerSpec extends UnitTest {

  trait Context extends Scope {
    val request = Request(Method.Get, "/foo/bar")
    val response = Response(Status.Ok)
    val labeller = new HttpServiceLabeller()
    val labels = labeller.labelsFor(request, response)
  }

  "keys" >> {
    "returns the keys in the correct order" in new Context {
      labeller.keys ==== Seq("status", "statusClass", "method")
    }
  }

  "labelsFor" >> {
    "returns the status code of the response" in new Context {
      labels(0) ==== "200"
    }

    "returns the status class of the request" in new Context {
      labels(1) ==== "2xx"
    }

    "returns the method of the request" in new Context {
      labels(2) ==== "GET"
    }
  }
} 
Example 24
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 25
Source File: CrossFieldValidation.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package examples.validation

import com.twitter.finagle.Service
import com.twitter.finagle.http.Method.Get
import com.twitter.finagle.http.Request
import com.twitter.finagle.http.path.Root
import com.twitter.util.Await.result
import io.fintrospect.formats.PlainText.ResponseBuilder._
import io.fintrospect.parameters.Query
import io.fintrospect.util.HttpRequestResponseUtil.statusAndContentFrom
import io.fintrospect.util.{Extracted, ExtractionFailed, Extractor}
import io.fintrospect.{RouteModule, RouteSpec}

case class Person(gender: Option[String], experience: Int)

case class SchoolClass(pupils: Int, teacher: Person)


object CrossFieldValidation extends App {
  type Predicate[T] = T => Boolean

  // lower level extractor: extracts a person from the request
  val person: Extractor[Request, Person] = Extractor.mk {
    req: Request =>
      for {
        gender <- Query.optional.string("gender") <--? req
        exp <- Query.required.int("experience") <--? req
      } yield Person(gender, exp)
  }

  // higher-level extractor: uses other extractors and validation rules
  val acceptableClassSize: Extractor[Request, SchoolClass] = {

    // this is a cross-field validation rule, which is basically a predicate and a reason for failure
    def lessThanYearsExperience(teacher: Person): Predicate[Int] = number => teacher.experience > number

    Extractor.mk {
      req: Request =>
        for {
          teacher <- person <--? req
          pupils <- Query.required.int("pupils") <--? (req, "Too many pupils", lessThanYearsExperience(teacher))
        } yield {
          SchoolClass(pupils, teacher)
        }
    }
  }

  // HTTP route which applies the validation - returning the overall Extraction result in case of success
  val checkClassSize = RouteSpec().at(Get) bindTo Service.mk {
    req: Request => {
      acceptableClassSize <--? req match {
        case Extracted(clazz) => Ok(clazz.toString)
        case ExtractionFailed(sp) => BadRequest(sp.mkString(", "))
      }
    }
  }

  val svc = RouteModule(Root).withRoute(checkClassSize).toService

  // print succeeding and failing cases
  println("Missing parameters case: " + statusAndContentFrom(result(svc(Request("?gender=male&pupils=10")))))
  println("Failing logic case: " + statusAndContentFrom(result(svc(Request("?gender=male&experience=9&pupils=10")))))
  println("Successful case: " + statusAndContentFrom(result(svc(Request("?gender=female&experience=16&pupils=15")))))

} 
Example 26
Source File: TemplatingApp.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package examples.templating

import java.net.URL

import com.twitter.finagle.http.Method.Get
import com.twitter.finagle.http.Request
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, Service}
import com.twitter.util.Await
import io.fintrospect.formats.PlainText
import io.fintrospect.renderers.SiteMapModuleRenderer
import io.fintrospect.templating.{MustacheTemplates, RenderView}
import io.fintrospect.{RouteModule, RouteSpec}

object TemplatingApp extends App {

  val devMode = true
  val renderer = if (devMode) MustacheTemplates.HotReload("src/main/resources") else MustacheTemplates.CachingClasspath(".")

  val renderView = new RenderView(PlainText.ResponseBuilder, renderer)

  val module = RouteModule(Root, new SiteMapModuleRenderer(new URL("http://my.cool.app")), renderView)
    .withRoute(RouteSpec().at(Get) / "echo" bindTo Service.mk { rq: Request => MustacheView(rq.uri) })

  println("See the Sitemap description at: http://localhost:8181")

  Await.ready(
    Http.serve(":8181", new HttpFilter(Cors.UnsafePermissivePolicy).andThen(module.toService))
  )
} 
Example 27
Source File: StrictMultiContentTypeRoute.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package examples.strictcontenttypes

import com.twitter.finagle.http.Method.Get
import com.twitter.finagle.http.Request
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, Service}
import com.twitter.util.Await
import io.fintrospect.ContentTypes.{APPLICATION_JSON, APPLICATION_XML}
import io.fintrospect.filters.RequestFilters
import io.fintrospect.parameters.Path
import io.fintrospect.renderers.simplejson.SimpleJson
import io.fintrospect.util.StrictContentTypeNegotiation
import io.fintrospect.{RouteModule, RouteSpec}



object StrictMultiContentTypeRoute extends App {

  private def serveJson(name: String) = Service.mk { req: Request =>
    import io.fintrospect.formats.Argo.JsonFormat._
    import io.fintrospect.formats.Argo.ResponseBuilder._
    Ok(obj("field" -> string(name)))
  }

  private def serveXml(name: String) = Service.mk {
    import io.fintrospect.formats.Xml.ResponseBuilder._
    req: Request =>
      Ok(<root>
        <field>
          {name}
        </field>
      </root>)
  }

  val route = RouteSpec()
    .producing(APPLICATION_XML, APPLICATION_JSON)
    .at(Get) / "multi" / Path.string("name") bindTo StrictContentTypeNegotiation(APPLICATION_XML -> serveXml, APPLICATION_JSON -> serveJson)

  val jsonOnlyRoute = RouteSpec()
    .producing(APPLICATION_JSON)
    .at(Get) / "json" / Path.string("name") bindTo ((s) => RequestFilters.StrictAccept(APPLICATION_JSON).andThen(serveJson(s)))

  println("See the service description at: http://localhost:8080 . The route at /multi should match wildcard Accept headers set in a browser.")

  Await.ready(
    Http.serve(":8080", new HttpFilter(Cors.UnsafePermissivePolicy)
      .andThen(RouteModule(Root, SimpleJson()).withRoute(route).toService))
  )
} 
Example 28
Source File: MultiBodyTypeRoute.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package examples.strictcontenttypes

import com.twitter.finagle.http.Method.Post
import com.twitter.finagle.http.Request
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, Service}
import com.twitter.util.Await
import io.fintrospect.parameters.Body
import io.fintrospect.renderers.simplejson.SimpleJson
import io.fintrospect.util.MultiBodyType
import io.fintrospect.{RouteModule, RouteSpec}


object MultiBodyTypeRoute extends App {

  private val json = Body.json("json body")

  private val echoJson = Service.mk { (rq: Request) =>
    import io.fintrospect.formats.Argo.ResponseBuilder._
    Ok(json <-- rq)
  }

  private val xml = Body.xml("xml body")

  private val echoXml = Service.mk { (rq: Request) =>
    import io.fintrospect.formats.Xml.ResponseBuilder._
    Ok(xml <-- rq)
  }

  val route = RouteSpec("echo posted content in either JSON or XML").at(Post) / "echo" bindTo MultiBodyType(json -> echoJson, xml -> echoXml)

  println("See the service description at: http://localhost:8080")

  Await.ready(
    Http.serve(":8080", new HttpFilter(Cors.UnsafePermissivePolicy)
      .andThen(RouteModule(Root, SimpleJson()).withRoute(route).toService))
  )
} 
Example 29
Source File: ContractProxyExample.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package examples.clients

import com.twitter.finagle.http.Method.Get
import com.twitter.finagle.http.filter.Cors
import com.twitter.finagle.http.filter.Cors.HttpFilter
import com.twitter.finagle.http.{Request, Response}
import com.twitter.finagle.{Http, Service}
import com.twitter.util.Await
import io.fintrospect.configuration.{Credentials, Host, Port}
import io.fintrospect.filters.RequestFilters.{AddHost, BasicAuthorization}
import io.fintrospect.parameters.Query
import io.fintrospect.{Contract, ContractEndpoint, ContractProxyModule, RouteSpec}


object ContractProxyExample extends App {

  val proxyModule = ContractProxyModule("brewdog", BrewdogApiHttp(), BrewdogApiContract)

  Await.ready(
    Http.serve(":9000", new HttpFilter(Cors.UnsafePermissivePolicy).andThen(proxyModule.toService))
  )
}

object BrewdogApiHttp {
  private val apiAuthority = Host("punkapi.com").toAuthority(Port(443))

  def apply(): Service[Request, Response] = {
    AddHost(apiAuthority)
      .andThen(BasicAuthorization(Credentials("22244d6b88574064bbbfe284f1631eaf", "")))
      .andThen(Http.client.withTlsWithoutValidation.newService(apiAuthority.toString))
  }
}

object BrewdogApiContract extends Contract {

  object LookupBeers extends ContractEndpoint {
    val brewedBefore = Query.optional.string("brewed_before", "e.g. 01-2010 (format is mm-yyyy)")
    val alcoholContent = Query.optional.int("abv_gt", "Minimum alcohol %")

    override val route =
      RouteSpec("lookup beers")
        .taking(brewedBefore)
        .taking(alcoholContent)
        .at(Get) / "api" / "v1" / "beers"
  }

  object RandomBeer extends ContractEndpoint {
    override val route = RouteSpec("get a random beer recipe")
      .at(Get) / "api" / "v1" / "beers" / "random"
  }

} 
Example 30
Source File: ClientSideAndSharedRouteSpecExample.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package examples.clients

import java.time.LocalDate

import com.twitter.finagle.http.Method.Get
import com.twitter.finagle.http.{Request, Response}
import com.twitter.finagle.{Http, Service}
import com.twitter.util.Await
import io.fintrospect.RouteSpec
import io.fintrospect.formats.PlainText.ResponseBuilder._
import io.fintrospect.parameters._
import io.fintrospect.testing.TestHttpServer
import io.fintrospect.util.HttpRequestResponseUtil.{headersFrom, statusAndContentFrom}


object ClientSideAndSharedRouteSpecExample extends App {

  val theDate = Path.localDate("date")
  val theWeather = Query.optional.string("weather")
  val theUser = Header.required.string("user")
  val gender = FormField.optional.string("gender")
  val body = Body.form(gender)

  val sharedRouteSpec = RouteSpec()
    .taking(theUser)
    .taking(theWeather)
    .body(body)
    .at(Get) / "firstSection" / theDate

  val fakeServerRoute = sharedRouteSpec bindTo (dateFromPath => Service.mk[Request, Response] {
    request: Request => {
      println("URL was " + request.uri)
      println("Headers were " + headersFrom(request))
      println("Form sent was " + (body <-- request))
      println("Date send was " + dateFromPath.toString)
      Ok(dateFromPath.toString)
    }
  })

  Await.result(new TestHttpServer(10000, fakeServerRoute).start())

  val client = sharedRouteSpec bindToClient Http.newService("localhost:10000")

  val theCall = client(theWeather --> Option("sunny"), body --> Form(gender --> "male"), theDate --> LocalDate.of(2015, 1, 1), theUser --> System.getenv("USER"))

  val response = Await.result(theCall)

  println("Response headers: " + headersFrom(response))
  println("Response: " + statusAndContentFrom(response))
} 
Example 31
Source File: BookAdd.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package examples.extended

import com.twitter.finagle.Service
import com.twitter.finagle.http.{Method, Request, Response, Status}
import io.fintrospect.formats.Argo.ResponseBuilder._
import io.fintrospect.parameters.{Body, Path}
import io.fintrospect.{ResponseSpec, RouteSpec}

class BookAdd(books: Books) {
  private val exampleBook = Book("the title", "the author", 666)
  private val bookExistsResponse = Conflict("Book with that ISBN exists")
  private val jsonBody = Body.json("book content", exampleBook.toJson)

  private def addBook(isbn: String) = Service.mk {
    request: Request =>
      books.lookup(isbn) match {
        case Some(_) => bookExistsResponse
        case None => {
          val book = Book.unapply(jsonBody <-- request).get
          books.add(isbn, book)
          Created(book.toJson)
        }
      }
  }

  val route = RouteSpec("add book by isbn number", "This book must not already exist")
    .body(jsonBody)
    .returning(ResponseSpec.json(Status.Created -> "we added your book", exampleBook.toJson))
    .returning(bookExistsResponse)
    .at(Method.Post) / "book" / Path.string("isbn", "the isbn of the book") bindTo addBook
} 
Example 32
Source File: BookLengthSearch.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package examples.extended

import java.lang.Integer.MIN_VALUE

import com.twitter.finagle.Service
import com.twitter.finagle.http.Method.Post
import com.twitter.finagle.http.{Request, Status}
import io.fintrospect.ContentTypes.APPLICATION_JSON
import io.fintrospect.RouteSpec
import io.fintrospect.formats.Argo.JsonFormat.array
import io.fintrospect.formats.Argo.ResponseBuilder._
import io.fintrospect.parameters.{Body, FormField}

class BookLengthSearch(books: Books) {
  private val minPages = FormField.optional.int("minPages", "min number of pages in book")
  private val maxPages = FormField.required.int("maxPages", "max number of pages in book")
  private val form = Body.form(minPages, maxPages)

  private val search = Service.mk {
    request: Request => {
      val requestForm = form <-- request
      Ok(array(books.search(minPages <-- requestForm getOrElse MIN_VALUE, maxPages <-- requestForm, Seq("")).map(_.toJson)))
    }
  }

  val route = RouteSpec("search for books by number of pages", "This won't work in Swagger because it's a form... :(")
    .body(form)
    .returning(Status.Ok -> "we found some books", array(Book("a book", "authorName", 99).toJson))
    .returning(Status.BadRequest -> "invalid request")
    .producing(APPLICATION_JSON)
    .at(Post) / "lengthSearch" bindTo search
} 
Example 33
Source File: BookTermSearch.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package examples.extended

import java.lang.Integer.{MAX_VALUE, MIN_VALUE}

import com.twitter.finagle.Service
import com.twitter.finagle.http.Method.Get
import com.twitter.finagle.http.{Request, Status}
import io.fintrospect.ContentTypes.APPLICATION_JSON
import io.fintrospect.RouteSpec
import io.fintrospect.formats.Argo.JsonFormat.array
import io.fintrospect.formats.Argo.ResponseBuilder._
import io.fintrospect.parameters.Query

class BookTermSearch(books: Books) {
  private val titleTerms = Query.required.*.string("term", "parts of the title to look for")

  private val search = Service.mk { request: Request =>
    Ok(array(books.search(MIN_VALUE, MAX_VALUE, titleTerms <-- request).map(_.toJson)))
  }

  val route = RouteSpec("search for book by title fragment")
    .taking(titleTerms)
    .returning(Status.Ok -> "we found some books", array(Book("a book", "authorName", 99).toJson))
    .producing(APPLICATION_JSON)
    .at(Get) / "titleSearch" bindTo search
} 
Example 34
Source File: BookLookup.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package examples.extended

import com.twitter.finagle.Service
import com.twitter.finagle.http.Method.Get
import com.twitter.finagle.http.{Request, Response, Status}
import io.fintrospect.ContentTypes.APPLICATION_JSON
import io.fintrospect.RouteSpec
import io.fintrospect.formats.Argo.ResponseBuilder._
import io.fintrospect.parameters.Path

class BookLookup(books: Books) {

  private def lookupByIsbn(isbn: String) = Service.mk {
    request: Request =>
      books.lookup(isbn) match {
        case Some(book) => Ok(book.toJson)
        case _ => NotFound("No book found with isbn")
      }
  }

  val route = RouteSpec("lookup book by isbn number")
    .producing(APPLICATION_JSON)
    .returning(NotFound("no book was found with this ISBN"))
    .returning(Status.Ok -> "we found your book", Book("a book", "authorName", 99).toJson)
    .at(Get) / "book" / Path.string("isbn", "the isbn of the book") bindTo lookupByIsbn
} 
Example 35
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 36
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 37
Source File: BookLookupTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package examples.extended

import com.twitter.finagle.http.Request
import com.twitter.finagle.http.Status.NotFound
import io.fintrospect.formats.Argo.JsonFormat.parse
import io.fintrospect.testing.TestingFintrospectRoute
import io.fintrospect.util.HttpRequestResponseUtil.statusAndContentFrom
import org.scalatest.{FunSpec, Matchers}


class BookLookupTest extends FunSpec with Matchers with TestingFintrospectRoute {

  override val route = new BookLookup(new Books()).route

  describe("Book Lookup") {
    it("can lookup an existing book") {
      parse(responseFor(Request("/book/hp1")).contentString) shouldBe Book("hairy porker", "j.k oinking", 799).toJson
    }
  }

  it("non-existing book") {
    statusAndContentFrom(responseFor(Request("/book/hp8")))._1 shouldBe NotFound
  }
} 
Example 38
Source File: FakeRemoteLibrary.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package presentation._6

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}
import com.twitter.finagle.{Http, Service}
import io.fintrospect.RouteModule
import io.fintrospect.formats.PlainText.ResponseBuilder._
import io.fintrospect.renderers.simplejson.SimpleJson
import presentation.Books


class FakeRemoteLibrary(books: Books) {
  def search(titlePart: String) = Service.mk[Request, Response] {
    request => {
      val results = books.titles().filter(_.toLowerCase.contains(titlePart.toLowerCase))
      Ok(results.mkString(","))
    }
  }

  val service = RouteModule(Root, SimpleJson())
    .withRoute(RemoteBooks.route bindTo search)
    .toService

  val searchService = new HttpFilter(Cors.UnsafePermissivePolicy).andThen(service)
  Http.serve(":10000", searchService)
} 
Example 39
Source File: SearchApp.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package presentation._6

import com.twitter.finagle.http.Method.{Get, 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 io.fintrospect.formats.Argo.JsonFormat.array
import io.fintrospect.formats.Argo.ResponseBuilder._
import io.fintrospect.parameters.{Body, BodySpec, Query}
import io.fintrospect.renderers.swagger2dot0.{ApiInfo, Swagger2dot0Json}
import io.fintrospect.{ResponseSpec, RouteModule, RouteSpec}
import presentation.Book

class SearchRoute(books: RemoteBooks) {
  private val titlePartParam = Query.required.string("titlePart")

  def search() = Service.mk[Request, Response] {
    request => {
      val titlePart = titlePartParam <-- request

      books.search(titlePart)
        .map(results => results.split(",").map(Book(_)).toSeq)
        .map(books => Ok(array(books.map(_.toJson))))
    }
  }

  val route = RouteSpec("search books")
    .taking(titlePartParam)
    .returning(ResponseSpec.json(Status.Ok -> "search results", array(Book("1984").toJson)))
    .at(Get) / "search" bindTo search
}

class BookAvailable(books: RemoteBooks) {
  private val bodySpec = BodySpec.json().map(Book.fromJson, (b: Book) => b.toJson)
  private val body = Body.of(bodySpec, "a book", Book("1984"))

  def availability() = Service.mk[Request, Response] {
    request => {
      val book = body <-- request
      books.search(book.title)
        .map(results => {
          if (results.length > 0) Ok("cool") else NotFound("!")
        })
    }
  }

  val route = RouteSpec("find if the book is owned")
    .body(body)
    .returning(Status.Ok -> "book is available")
    .returning(Status.NotFound -> "book not found")
    .at(Post) / "availability" bindTo availability
}


class SearchApp {
  private val apiInfo = ApiInfo("search some books", "1.0", "an api for searching our book collection")

  val service = RouteModule(Root, Swagger2dot0Json(apiInfo))
    .withRoute(new SearchRoute(new RemoteBooks).route)
    .withRoute(new BookAvailable(new RemoteBooks).route)
    .toService

  val searchService = new HttpFilter(Cors.UnsafePermissivePolicy).andThen(service)
  Http.serve(":9000", searchService)
} 
Example 40
Source File: FakeRemoteLibrary.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package presentation._5

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}
import com.twitter.finagle.{Http, Service}
import io.fintrospect.RouteModule
import io.fintrospect.formats.PlainText.ResponseBuilder._
import io.fintrospect.renderers.simplejson.SimpleJson
import presentation.Books


class FakeRemoteLibrary(books: Books) {
  def search(titlePart: String) = Service.mk[Request, Response] {
    request => Ok(books.titles().filter(_.toLowerCase.contains(titlePart.toLowerCase)).mkString(","))
  }

  val service = RouteModule(Root, SimpleJson())
    .withRoute(RemoteBooks.route bindTo search)
    .toService

  val searchService = new HttpFilter(Cors.UnsafePermissivePolicy).andThen(service)
  Http.serve(":10000", searchService)
} 
Example 41
Source File: SearchApp.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package presentation._5

import com.twitter.finagle.http.Method.Get
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 io.fintrospect.formats.Argo.JsonFormat.array
import io.fintrospect.formats.Argo.ResponseBuilder._
import io.fintrospect.parameters.Query
import io.fintrospect.renderers.swagger2dot0.{ApiInfo, Swagger2dot0Json}
import io.fintrospect.{ResponseSpec, RouteModule, RouteSpec}
import presentation.Book

class SearchRoute(books: RemoteBooks) {
  private val titlePartParam = Query.required.string("titlePart")

  def search() = Service.mk[Request, Response] {
    request => {
      val titlePart = titlePartParam <-- request

      books.search(titlePart)
        .map(results => results.split(",").map(Book(_)).toSeq)
        .map(books => Ok(array(books.map(_.toJson))))
    }
  }

  val route = RouteSpec("search books")
    .taking(titlePartParam)
    .returning(ResponseSpec.json(Status.Ok -> "search results", array(Book("1984").toJson)))
    .at(Get) / "search" bindTo search
}


class SearchApp {
  private val apiInfo = ApiInfo("search some books", "1.0", "an api for searching our book collection")

  val service = RouteModule(Root, Swagger2dot0Json(apiInfo))
    .withRoute(new SearchRoute(new RemoteBooks).route)
    .toService

  val searchService = new HttpFilter(Cors.UnsafePermissivePolicy).andThen(service)
  Http.serve(":9000", searchService)
} 
Example 42
Source File: FakeRemoteLibrary.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package presentation._4

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}
import com.twitter.finagle.{Http, Service}
import io.fintrospect.RouteModule
import io.fintrospect.formats.PlainText.ResponseBuilder._
import io.fintrospect.renderers.simplejson.SimpleJson
import presentation.Books


class FakeRemoteLibrary(books: Books) {
  def search(titlePart: String) = Service.mk[Request, Response] {
    _ => Ok(books.titles().filter(_.toLowerCase.contains(titlePart.toLowerCase)).mkString(","))
  }

  val service = RouteModule(Root, SimpleJson())
    .withRoute(RemoteBooks.route bindTo search)
    .toService

  val searchService = new HttpFilter(Cors.UnsafePermissivePolicy).andThen(service)
  Http.serve(":10000", searchService)
} 
Example 43
Source File: SearchApp.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package presentation._4

import com.twitter.finagle.http.Method.Get
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}
import com.twitter.finagle.{Http, Service}
import io.fintrospect.formats.PlainText.ResponseBuilder._
import io.fintrospect.parameters.Query
import io.fintrospect.renderers.swagger2dot0.{ApiInfo, Swagger2dot0Json}
import io.fintrospect.{RouteModule, RouteSpec}

class SearchRoute(books: RemoteBooks) {
  private val titlePartParam = Query.required.string("titlePart")

  def search() = Service.mk[Request, Response] {
    request => {
      val titlePart = titlePartParam <-- request

      books.search(titlePart)
        .map(results => Ok(results))
    }
  }

  val route = RouteSpec("search books")
    .taking(titlePartParam)
    .at(Get) / "search" bindTo search
}


class SearchApp {
  private val apiInfo = ApiInfo("search some books", "1.0", "an api for searching our book collection")

  val service = RouteModule(Root, Swagger2dot0Json(apiInfo))
    .withRoute(new SearchRoute(new RemoteBooks).route)
    .toService

  val searchService = new HttpFilter(Cors.UnsafePermissivePolicy).andThen(service)
  Http.serve(":9000", searchService)
} 
Example 44
Source File: SearchApp.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package presentation._3

import com.twitter.finagle.http.Method.Get
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}
import com.twitter.finagle.{Http, Service}
import io.fintrospect.formats.PlainText.ResponseBuilder._
import io.fintrospect.parameters.Query
import io.fintrospect.renderers.swagger2dot0.{ApiInfo, Swagger2dot0Json}
import io.fintrospect.{RouteModule, RouteSpec}
import presentation.Books

class SearchRoute(books: Books) {
  private val titlePartParam = Query.required.string("titlePart")

  def search() = Service.mk[Request, Response] {
    request => {
      val titlePart = titlePartParam <-- request
      val results = books.titles().filter(_.toLowerCase.contains(titlePart.toLowerCase))
      Ok(results.toString())
    }
  }

  val route = RouteSpec("search books")
    .taking(titlePartParam)
    .at(Get) / "search" bindTo search
}


class SearchApp(books: Books) {
  private val apiInfo = ApiInfo("search some books", "1.0", "an api for searching our book collection")

  val service = RouteModule(Root, Swagger2dot0Json(apiInfo))
    .withRoute(new SearchRoute(books).route)
    .toService

  val searchService = new HttpFilter(Cors.UnsafePermissivePolicy).andThen(service)
  Http.serve(":9000", searchService)
}


object Environment extends App {
  new SearchApp(new Books)
  Thread.currentThread().join()
}

 
Example 45
Source File: SearchApp.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package presentation._2

import com.twitter.finagle.http.Method.Get
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}
import com.twitter.finagle.{Http, Service}
import io.fintrospect.formats.PlainText.ResponseBuilder._
import io.fintrospect.renderers.swagger2dot0.{ApiInfo, Swagger2dot0Json}
import io.fintrospect.{RouteModule, RouteSpec}
import presentation.Books

class SearchApp(books: Books) {
  def search() = Service.mk[Request, Response] { _ => Ok(books.titles().toString()) }

  private val apiInfo = ApiInfo("search some books", "1.0", "an api for searching our book collection")

  val service = RouteModule(Root, Swagger2dot0Json(apiInfo))
    .withRoute(RouteSpec("search books").at(Get) / "search" bindTo search)
    .toService

  val searchService = new HttpFilter(Cors.UnsafePermissivePolicy).andThen(service)
  Http.serve(":9000", searchService)
}


object Environment extends App {
  new SearchApp(new Books)
  Thread.currentThread().join()
}

 
Example 46
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 47
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 48
Source File: Simple_Form_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.forms


// fintrospect-core
object Simple_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 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.form(nameField, ageField)

  val svc: Service[Request, Response] = Service.mk[Request, Response] {
    req => {
      val formInstance: Form = form <-- req
      val name: String = nameField <-- formInstance
      val age: Option[Int] = ageField <-- formInstance
      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 49
Source File: MultiPart_Form_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.forms


// fintrospect-core
object MultiPart_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 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.multiPartForm(usernameField, fileField)

  val svc: Service[Request, Response] = Service.mk[Request, Response] {
    req => {
      val postedForm: Form = form <-- req
      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 50
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 51
Source File: Handlebars_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.templating

// fintrospect-core
// fintrospect-handlebars
case class HandlebarsView(name: String, age: Int) extends io.fintrospect.templating.View

object Handlebars_Example extends App {

  import com.twitter.finagle.http.Method.Get
  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 io.fintrospect.formats.Html
  import io.fintrospect.parameters.Path
  import io.fintrospect.templating.{HandlebarsTemplates, RenderView, View}
  import io.fintrospect.{Module, RouteModule, RouteSpec, ServerRoute}

  def showAgeIn30(name: String, age: Int): Service[Request, Response] = {
    val svc = Service.mk[Request, View] { req => MustacheView(name, age + 30) }

    new RenderView(Html.ResponseBuilder, HandlebarsTemplates.CachingClasspath(".")).andThen(svc)
  }

  val route: ServerRoute[Request, Response] = RouteSpec()
    .at(Get) / Path.string("name") / Path.int("age") bindTo showAgeIn30

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

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

//curl -v http://localhost:9999/david/100 
Example 52
Source File: Semi_Auto_Marshalling_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.json_libraries

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

// fintrospect-core
// fintrospect-circe
object Semi_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 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.Body
  import io.fintrospect.{Module, RouteModule, RouteSpec, ServerRoute}

  val personBody = Body.of(Circe.bodySpec[Person]())

  val insultMe: Service[Request, Response] = Service.mk[Request, Response] { req =>
    val person: Person = personBody <-- req
    val smellyPerson: Person = person.copy(name = person.name + " Smells")
    Ok(encode(smellyPerson))
  }

  val route: ServerRoute[Request, Response] = RouteSpec()
    .body(personBody)
    .at(Post) bindTo 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 53
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 54
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 55
Source File: Composite_Request_Parameters_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.core


// fintrospect-core
object Composite_Request_Parameters_Example extends App {

  import com.twitter.finagle.http.Method.Get
  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 io.fintrospect.formats.PlainText.ResponseBuilder._
  import io.fintrospect.parameters.{Binding, Composite, Header, Query}
  import io.fintrospect.util.Extraction
  import io.fintrospect.{Module, RouteModule, RouteSpec, ServerRoute}



  case class FooBar(foo: String, bar: Int)

  object FooBar extends Composite[FooBar] {
    private val fooQ = add(Header.required.string("foo"))
    private val barQ = add(Query.required.int("bar"))

    override def -->(foobar: FooBar): Iterable[Binding] = (fooQ --> foobar.foo) ++ (barQ --> foobar.bar)

    override def <--?(req: Request): Extraction[FooBar] = {
      for {
        foo <- fooQ <--? req
        bar <- barQ <--? req
      } yield FooBar(foo, bar)
    }
  }

  val route: ServerRoute[Request, Response] = RouteSpec()
    .taking(FooBar).at(Get) bindTo Service.mk {
    req: Request => Ok("you sent: " + (FooBar <-- req))
  }

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

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

//curl -v -H"foo: foo" http://localhost:9999?bar=123 
Example 56
Source File: Serving_Multiple_Content_Types_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.core

// fintrospect-core
object Serving_Multiple_Content_Types_Example extends App {

  import com.twitter.finagle.http.Method.Get
  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 io.fintrospect.ContentTypes.{APPLICATION_JSON, APPLICATION_XML}
  import io.fintrospect.parameters.Path
  import io.fintrospect.util.StrictContentTypeNegotiation
  import io.fintrospect.{RouteModule, RouteSpec}

  def serveJson(name: String) = Service.mk[Request, Response] { req =>
    import io.fintrospect.formats.Argo.JsonFormat._
    import io.fintrospect.formats.Argo.ResponseBuilder._
    Ok(obj("field" -> string(name)))
  }

  def serveXml(name: String) = Service.mk[Request, Response] {
    import io.fintrospect.formats.Xml.ResponseBuilder._
    req =>
      Ok(<root>
        <field>
          {name}
        </field>
      </root>)
  }

  val route = RouteSpec()
    .at(Get) / Path.string("name") bindTo
      StrictContentTypeNegotiation(APPLICATION_XML -> serveXml, APPLICATION_JSON -> serveJson)

  ready(Http.serve(":9999", RouteModule(Root).withRoute(route).toService))
}
//curl -v -H"Accept: application/json" http://localhost:9999/David
//curl -v -H"Accept: application/xml" http://localhost:9999/David 
Example 57
Source File: Simple_XML_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.core

// fintrospect-core
object Simple_XML_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 io.fintrospect.formats.Xml.ResponseBuilder._
  import io.fintrospect.parameters.Body
  import io.fintrospect.{Module, RouteModule, RouteSpec, ServerRoute}

  import scala.xml.Elem

  val document: Body[Elem] = Body.xml()

  val analyse: Service[Request, Response] = Service.mk[Request, Response] {
    req => {
      val postedDoc: Elem = document <-- req
      Ok(
        <document>
          <number-of-root-elements>
            {postedDoc.length}
          </number-of-root-elements>
        </document>
      )
    }
  }

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

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

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

//curl -v -XPOST http://localhost:9999/ --data '<person name="david" age="100"/>' 
Example 58
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 59
Source File: Custom_Response_Format_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.core


// fintrospect-core
object Custom_Response_Format_Example extends App {
  import com.twitter.finagle.http.Method.Get
  import com.twitter.finagle.http.path.Root
  import com.twitter.finagle.http.{Request, Response}
  import com.twitter.finagle.{Http, Service}
  import com.twitter.io.Bufs
  import com.twitter.util.Await.ready
  import io.fintrospect.formats.{AbstractResponseBuilder, ResponseBuilder}
  import io.fintrospect.{ContentType, Module, RouteModule, RouteSpec, ServerRoute}

  object Csv {
    object ResponseBuilder extends AbstractResponseBuilder[List[String]] {
      override def HttpResponse(): ResponseBuilder[List[String]] = new ResponseBuilder(
        (i: List[String]) => Bufs.utf8Buf(i.mkString(",")),
        error => List("ERROR:" + error),
        throwable => List("ERROR:" + throwable.getMessage),
        ContentType("application/csv")
      )
    }
  }

  import Csv.ResponseBuilder._

  val service: Service[Request, Response] = Service.mk { _: Request => Ok(List("this", "is", "comma", "separated", "hello", "world")) }

  val route: ServerRoute[Request, Response] = RouteSpec().at(Get) bindTo service
  val module: Module = RouteModule(Root).withRoute(route)

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

//curl -v http://localhost:9999 
Example 60
Source File: Simple_HTML_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.core


// fintrospect-core
object Simple_HTML_Example extends App {

  import com.twitter.finagle.http.Method.Get
  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 io.fintrospect.formats.Html.ResponseBuilder._
  import io.fintrospect.{Module, RouteModule, RouteSpec, ServerRoute}

  val serve: Service[Request, Response] = Service.mk[Request, Response] {
    req => {
      Ok(
        <html>
          <head>
            <title>The Title</title>
          </head>
          <body>Some content goes here</body>
        </html>.toString()
      )
    }
  }

  val route: ServerRoute[Request, Response] = RouteSpec().at(Get) bindTo serve

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

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

//curl -v http://localhost:9999/' 
Example 61
Source File: Accepting_Multiple_Body_Types_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.core

// fintrospect-core
object Accepting_Multiple_Body_Types_Example extends App {

  import argo.jdom.JsonNode
  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 io.fintrospect.parameters.Body
  import io.fintrospect.util.MultiBodyType
  import io.fintrospect.{Module, RouteModule, RouteSpec, ServerRoute}

  import scala.xml.Elem

  val json: Body[JsonNode] = Body.json()

  val echoJson: Service[Request, Response] = Service.mk[Request, Response] { req =>
    import io.fintrospect.formats.Argo.ResponseBuilder._
    Ok(json <-- req)
  }

  val xml: Body[Elem] = Body.xml()

  val echoXml: Service[Request, Response] = Service.mk[Request, Response] { req =>
    import io.fintrospect.formats.Xml.ResponseBuilder._
    Ok(xml <-- req)
  }

  val route: ServerRoute[Request, Response] = RouteSpec("echo posted content in either JSON or XML")
    .at(Post) bindTo MultiBodyType(json -> echoJson, xml -> echoXml)

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

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

//curl -v -XPOST -H"Content-Type: application/json" http://localhost:9999/ --data '{"name":"David"}'
//curl -v -XPOST -H"Content-Type: application/xml" http://localhost:9999/ --data '<name>David</name>' 
Example 62
Source File: Module_Filters_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.core

// fintrospect-core
object Module_Filters_Example extends App {

  import com.twitter.finagle.http.Method.Get
  import com.twitter.finagle.http.path.Root
  import com.twitter.finagle.http.{Request, Response}
  import com.twitter.finagle.{Filter, Http, Service}
  import com.twitter.util.Await.ready
  import io.fintrospect.formats.PlainText.ResponseBuilder._
  import io.fintrospect.renderers.ModuleRenderer
  import io.fintrospect.{Module, RouteModule, RouteSpec, ServerRoute}

  val headers: Service[Request, Response] = Service.mk[Request, Response] { req => Ok(req.uri) }

  val route: ServerRoute[Request, Response] = RouteSpec().at(Get) bindTo headers

  val timingFilter = Filter.mk[Request, Response, Request, Response] {
    (req, next) =>
      val start = System.currentTimeMillis()
      next(req).map {
        resp => {
          val time = System.currentTimeMillis() - start
          resp.headerMap("performance") = s"${resp.contentString} took $time ms"
          resp
        }
      }
  }

  val module: Module = RouteModule(Root, ModuleRenderer.Default, timingFilter).withRoute(route)

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

//curl -v http://localhost:9999 
Example 63
Source File: Extracting_Upstream_Responses.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.core

// fintrospect-core
object Extracting_Upstream_Responses extends App {

  import argo.jdom.JsonNode
  import com.twitter.finagle.http.Method.Get
  import com.twitter.finagle.http.{Request, Response}
  import com.twitter.finagle.{Http, Service}
  import com.twitter.util.Await.result
  import io.fintrospect.configuration.{Authority, Host, Port}
  import io.fintrospect.filters.RequestFilters.AddHost
  import io.fintrospect.filters.ResponseFilters
  import io.fintrospect.parameters.{Body, BodySpec, Path}
  import io.fintrospect.util.{Extracted, Extraction, ExtractionFailed}
  import io.fintrospect.{RouteClient, RouteSpec}

  case class Pokemon(id: Int, name: String, weight: Int)

  object Pokemon {
    def from(j: JsonNode) = Pokemon(j.getNumberValue("id").toInt,
      j.getStringValue("name"),
      j.getNumberValue("weight").toInt)
  }

  val authority: Authority = Host("pokeapi.co").toAuthority(Port._80)

  val http: Service[Request, Response] = AddHost(authority).andThen(Http.newService(authority.toString))

  val id = Path.int("pokemonId")


  val spec: BodySpec[Pokemon] = BodySpec.json().map(Pokemon.from)

  val nameAndWeight: Body[Pokemon] = Body.of(spec)

  val client: RouteClient[Extraction[Option[Pokemon]]] = RouteSpec().at(Get) / "api" / "v2" / "pokemon" / id / "" bindToClient
    ResponseFilters.ExtractBody(nameAndWeight).andThen(http)

  def reportOnPokemon(pid: Int) =
    println(
      result(client(id --> pid)) match {
        case Extracted(Some(pokemon)) => s"i found a pokemon: $pokemon"
        case Extracted(None) => s"there is no pokemon with id $pid"
        case ExtractionFailed(errors) => s"problem extracting response $errors"
      }
    )

  reportOnPokemon(1)
  reportOnPokemon(11)
  reportOnPokemon(9999)
} 
Example 64
Source File: Simple_JSON_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.core


// fintrospect-core
object Simple_JSON_Example extends App {

  import java.time.ZonedDateTime

  import argo.jdom.JsonNode
  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 io.fintrospect.formats.Argo.JsonFormat._
  import io.fintrospect.formats.Argo.ResponseBuilder._
  import io.fintrospect.parameters.Body
  import io.fintrospect.{Module, RouteModule, RouteSpec, ServerRoute}

  val json: Body[JsonNode] = Body.json()

  val echo: Service[Request, Response] = Service.mk[Request, Response] { req =>
    val requestJson: JsonNode = json <-- req
    val responseJson: JsonNode = obj(
      "posted" -> requestJson,
      "time" -> string(ZonedDateTime.now().toString)
    )
    Ok(responseJson)
  }

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

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

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

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

// fintrospect-core
object Combining_Modules_Example extends App {

  import com.twitter.finagle.http.Method.Get
  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 io.fintrospect.formats.PlainText.ResponseBuilder._
  import io.fintrospect.{Module, RouteModule, RouteSpec, ServerRoute}

  val identify: Service[Request, Response] = Service.mk { req: Request => Ok(req.uri) }

  val route: ServerRoute[Request, Response] = RouteSpec().at(Get) bindTo identify
  val childModule: Module = RouteModule(Root / "child").withRoute(route)
  val rootModule: Module = RouteModule(Root).withRoute(route)

  ready(Http.serve(":9999", childModule.andThen(rootModule).toService))
}

//curl -v http://localhost:9999/child
//curl -v http://localhost:9999 
Example 66
Source File: Streaming_Response_Example.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package cookbook.core

// fintrospect-core
object Streaming_Response_Example extends App {

  import argo.jdom.JsonNode
  import com.twitter.concurrent.AsyncStream
  import com.twitter.finagle.http.Method.Get
  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.Duration.fromSeconds
  import com.twitter.util.Future.sleep
  import com.twitter.util.JavaTimer
  import io.fintrospect.formats.Argo.JsonFormat._
  import io.fintrospect.formats.Argo.ResponseBuilder._
  import io.fintrospect.{Module, RouteModule, RouteSpec, ServerRoute}

  val timer = new JavaTimer()

  def ints(i: Int): AsyncStream[Int] = i +:: AsyncStream.fromFuture(sleep(fromSeconds(1))(timer)).flatMap(_ => ints(i + 1))

  def jsonStream: AsyncStream[JsonNode] = ints(0).map(i => obj("number" -> number(i)))

  val count: Service[Request, Response] = Service.mk[Request, Response] { req => Ok(jsonStream) }

  val route: ServerRoute[Request, Response] = RouteSpec().at(Get) bindTo count

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

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

//curl --raw http://localhost:9999/ 
Example 67
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 68
Source File: JacksonTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.formats

import com.twitter.finagle.http.Request
import com.twitter.finagle.http.Status.Ok
import io.fintrospect.formats.Jackson.JsonFormat._
import io.fintrospect.formats.Jackson._
import io.fintrospect.parameters.{Body, Query}



class JacksonJsonResponseBuilderTest extends JsonResponseBuilderSpec(Jackson)

class JacksonJsonFormatTest extends JsonFormatSpec(Jackson) {
  override val expectedJson: String = """{"string":"hello","null":null,"bigInt":12344,"object":{"field1":"aString"},"decimal":1.2,"double":1.2,"array":["world",true],"long":2,"bool":true,"int":10}"""

  describe("Jackson.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("empty extracted JSON produces nulls") {
      decode[Letter](Jackson.JsonFormat.obj()) shouldBe Letter(null, null, null)
    }

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

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

    it("param spec decodes content") {
      val param = Query.required( parameterSpec[Letter](), "name")
      (param <-- Request("?name=" + encode(aLetter))) shouldBe aLetter
    }
  }
} 
Example 69
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 70
Source File: Binding.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.parameters

import com.twitter.finagle.http.Request
import io.fintrospect.RequestBuilder


sealed trait Binding {
  val parameter: Parameter

  def apply(requestBuild: RequestBuilder): RequestBuilder
}

class QueryBinding(val parameter: Parameter, value: String) extends Binding {
  def apply(requestBuild: RequestBuilder) = {
    val newValues = value +: requestBuild.queries.getOrElse(parameter.name, Nil)
    requestBuild.copy(queries = requestBuild.queries + (parameter.name -> newValues))
  }
}

class PathBinding(val parameter: Parameter, value: String) extends Binding {
  def apply(requestBuild: RequestBuilder) = requestBuild.copy(uriParts = requestBuild.uriParts :+ value)
}

class RequestBinding(val parameter: Parameter, into: Request => Request) extends Binding {
  def apply(requestBuild: RequestBuilder) = requestBuild.copy(fn = requestBuild.fn.andThen(into))
}

trait FormElementBinding extends (Form => Form)

class FormFieldBinding(parameter: Parameter, value: String)
  extends RequestBinding(parameter, identity) with FormElementBinding {
  override def apply(form: Form) = form + (parameter.name, value)
}

class FormFileBinding(parameter: Parameter, value: MultiPartFile)
  extends RequestBinding(parameter, identity) with FormElementBinding {
  def apply(form: Form) = form + (parameter.name, value)
} 
Example 71
Source File: HeaderParameter.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.parameters

import com.twitter.finagle.http.{Message, Request}

trait HeaderParameter[T]
  extends Parameter with Rebindable[Message, T, RequestBinding] {

  override val where = "header"
}

object HeaderExtractAndRebind extends ParameterExtractAndBind[Message, String, RequestBinding] {
  def newBinding(parameter: Parameter, value: String) = new RequestBinding(parameter, {
    req: Request => {
      req.headerMap.add(parameter.name, value)
      req
    }
  })

  def valuesFrom(parameter: Parameter, message: Message): Option[Seq[String]] = {
    val headers = message.headerMap.getAll(parameter.name)
    if (headers.isEmpty) None else Some(headers)
  }
} 
Example 72
Source File: Auto.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.formats

import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.finagle.{Filter, Service}
import io.fintrospect.parameters.{Body, Mandatory}
import io.fintrospect.util.{Extracted, ExtractionFailed}

class Auto[R](responseBuilder: AbstractResponseBuilder[R]) {

  type SvcBody[IN] = Body[IN] with Mandatory[Request, IN]

  import responseBuilder._

  
  def OptionalOut[IN, OUT](svc: Service[IN, Option[OUT]], successStatus: Status = Status.Ok)
                          (implicit transform: OUT => R): Service[IN, Response] = Filter.mk[IN, Response, IN, Option[OUT]] {
    (req, svc) =>
      svc(req)
        .map(
          _.map(transform)
            .map(l => HttpResponse(successStatus).withContent(l))
            .getOrElse(HttpResponse(Status.NotFound).withErrorMessage("No object available to unmarshal")))
        .map(_.build())
  }.andThen(svc)
} 
Example 73
Source File: ContractProxyModule.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.path.{Path, Root}
import com.twitter.finagle.http.{Request, Response}
import io.fintrospect.renderers.swagger2dot0.{ApiInfo, Swagger2dot0Json}


import scala.reflect.runtime.universe.TypeTag
import scala.reflect.runtime.{currentMirror, universe}



object ContractProxyModule {
  def apply[T <: Contract](name: String, service: Service[Request, Response], contract: T, rootPath: Path = Root, description: String = null)(implicit tag: TypeTag[T]): RouteModule[Request, Response] = {
    val descriptionOption = Option(description).getOrElse(s"Proxy services for $name API")
    val routes = universe.typeOf[T].members
      .filter(_.isModule)
      .map(_.asModule)
      .map(currentMirror.reflectModule(_).instance)
      .filter(_.isInstanceOf[ContractEndpoint])
      .map(_.asInstanceOf[ContractEndpoint].route)

    routes.foldLeft(RouteModule(rootPath, Swagger2dot0Json(ApiInfo(name, name, descriptionOption)))) {
      (spec, route) => spec.withRoute(route.bindToProxy(service))
    }
  }
} 
Example 74
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 75
Source File: StaticModule.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect

import com.google.common.io.Resources.toByteArray
import com.twitter.finagle.http.Method.Get
import com.twitter.finagle.http.Status.Ok
import com.twitter.finagle.http.path.{->, Path, Root}
import com.twitter.finagle.http.{Request, Response}
import com.twitter.finagle.{Filter, Service}
import com.twitter.io.Buf.ByteArray.Owned
import io.fintrospect.ContentType.lookup
import io.fintrospect.Module.ServiceBinding
import io.fintrospect.formats.ResponseBuilder.HttpResponse

object StaticModule {
  
  def apply(basePath: Path, resourceLoader: ResourceLoader = ResourceLoader.Classpath(), moduleFilter: Filter[Request, Response, Request, Response] = Filter.identity) = {
    new StaticModule(basePath, resourceLoader, moduleFilter)
  }
}

class StaticModule private(basePath: Path, resourceLoader: ResourceLoader, moduleFilter: Filter[Request, Response, Request, Response]) extends Module {

  override protected[fintrospect] def serviceBinding: ServiceBinding = {
    case Get -> path if exists(path) =>
      moduleFilter.andThen(Service.mk[Request, Response] {
        _ => HttpResponse(lookup(convertPath(path))).withCode(Ok).withContent(Owned(toByteArray(resourceLoader.load(convertPath(path)))))
      })
  }

  private def exists(path: Path) = if (path.startsWith(basePath)) resourceLoader.load(convertPath(path)) != null else false

  private def convertPath(path: Path) = {
    val newPath = if (basePath == Root) path.toString else path.toString.replace(basePath.toString, "")
    val resolved = if (newPath == "") "/index.html" else newPath
    resolved.replaceFirst("/", "")
  }
} 
Example 76
Source File: RequestBuilder.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 io.netty.handler.codec.http.QueryStringEncoder

case class RequestBuilder(method: Method,
                          uriParts: Seq[String] = Nil,
                          queries: Map[String, Seq[String]] = Map(),
                          fn: Request => Request = identity) {
  def build(): Request = {
    val baseUri = uriParts.mkString("/")
    val uri = queries.foldLeft(new QueryStringEncoder(if (baseUri.isEmpty) "/" else baseUri)) {
      (memo, q) =>
        q._2.foreach(v => memo.addParam(q._1, v))
        memo
    }.toString

    fn(Request(method, uri))
  }
} 
Example 77
Source File: ModuleSpec.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect

import com.twitter.finagle.Filter
import com.twitter.finagle.http.path.Path
import com.twitter.finagle.http.{Request, Response}
import io.fintrospect.renderers.ModuleRenderer


@deprecated("Renamed: use RouteModule instead", "13.16.0")
object ModuleSpec {
  @deprecated("Renamed: use RouteModule instead", "13.16.0")
  def apply(basePath: Path): RouteModule[Request, Response] = RouteModule(basePath)

  @deprecated("Renamed: use RouteModule instead", "13.16.0")
  def apply(basePath: Path, moduleRenderer: ModuleRenderer): RouteModule[Request, Response] = RouteModule(basePath, moduleRenderer)

  @deprecated("Renamed: use RouteModule instead", "13.16.0")
  def apply[RQ, RS](basePath: Path, moduleRenderer: ModuleRenderer, moduleFilter: Filter[Request, Response, RQ, RS]): RouteModule[RQ, RS] =
    RouteModule(basePath, moduleRenderer, moduleFilter)
} 
Example 78
Source File: SiteMapModuleRenderer.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.http.Method.Get
import com.twitter.finagle.http.path.Path
import com.twitter.finagle.http.{Request, Response, Status}
import io.fintrospect.formats.Xml.ResponseBuilder._
import io.fintrospect.util.ExtractionError
import io.fintrospect.{Security, ServerRoute}

class SiteMapModuleRenderer(baseUrl: URL) extends ModuleRenderer {

  override def badRequest(badParameters: Seq[ExtractionError]): Response = BadRequest(badParameters.toString())

  override def notFound(request: Request): Response = HttpResponse(Status.NotFound).build()

  override def description(basePath: Path, security: Security, routes: Seq[ServerRoute[_, _]]): Response = {
    def buildUrl(route: ServerRoute[_, _]) =
      <url>
        <loc>
          {baseUrl + route.describeFor(basePath)}
        </loc>
      </url>

    Ok(<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
      {routes.filter(_.method == Get).map(buildUrl)}
    </urlset>)
  }
} 
Example 79
Source File: SimpleJson.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.renderers.simplejson

import com.twitter.finagle.http.path.Path
import com.twitter.finagle.http.{Request, Response}
import io.fintrospect.formats.Argo
import io.fintrospect.formats.Argo.JsonFormat.{Field, obj}
import io.fintrospect.formats.Argo.ResponseBuilder._
import io.fintrospect.renderers.{JsonErrorResponseRenderer, ModuleRenderer}
import io.fintrospect.util.ExtractionError
import io.fintrospect.{Security, ServerRoute}


class SimpleJson extends ModuleRenderer {
  override def badRequest(badParameters: Seq[ExtractionError]): Response = JsonErrorResponseRenderer.badRequest(badParameters)

  override def notFound(request: Request): Response = JsonErrorResponseRenderer.notFound()

  private def render(basePath: Path, route: ServerRoute[_, _]): Field =
    route.method.toString() + ":" + route.describeFor(basePath) -> Argo.JsonFormat.string(route.routeSpec.summary)

  override def description(basePath: Path, security: Security, routes: Seq[ServerRoute[_, _]]): Response = Ok(obj("resources" -> obj(routes.map(r => render(basePath, r)))))
}

object SimpleJson {
  def apply() = new SimpleJson()
} 
Example 80
Source File: Swagger1dot1Json.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.renderers.swagger1dot1

import argo.jdom.JsonNode
import com.twitter.finagle.http.path.Path
import com.twitter.finagle.http.{Request, Response}
import io.fintrospect.formats.Argo.JsonFormat._
import io.fintrospect.formats.Argo.ResponseBuilder._
import io.fintrospect.parameters.HasParameters
import io.fintrospect.renderers.{JsonErrorResponseRenderer, ModuleRenderer}
import io.fintrospect.util.ExtractionError
import io.fintrospect.{Security, ServerRoute}


class Swagger1dot1Json extends ModuleRenderer {

  private def render(parameters: HasParameters): Iterable[JsonNode] = parameters.map(
    parameter =>
      obj(
        "name" -> string(parameter.name),
        "description" -> Option(parameter.description).map(string).getOrElse(nullNode()),
        "paramType" -> string(parameter.where),
        "required" -> boolean(parameter.required),
        "dataType" -> string(parameter.paramType.name)
      )
  )

  private def render(route: ServerRoute[_, _]): Field = route.method.toString().toLowerCase -> {
    val allParams =
      route.pathParams.flatten ++
        route.routeSpec.requestParams.flatMap(_.iterator).flatten ++
        route.routeSpec.body.map(_.iterator).getOrElse(Nil)

    obj(
      "Method" -> string(route.method.toString()),
      "nickname" -> string(route.routeSpec.summary),
      "notes" -> route.routeSpec.description.map(string).getOrElse(nullNode()),
      "produces" -> array(route.routeSpec.produces.map(m => string(m.value))),
      "consumes" -> array(route.routeSpec.consumes.map(m => string(m.value))),
      "parameters" -> array(allParams.flatMap(render)),
      "errorResponses" -> array(route.routeSpec.responses.values.filter(_.status.code > 399)
        .map(resp => obj("code" -> number(resp.status.code), "reason" -> string(resp.description))))
    )
  }

  override def description(basePath: Path, security: Security, routes: Seq[ServerRoute[_, _]]): Response = {
    Ok(obj("swaggerVersion" -> string("1.1"), "resourcePath" -> string("/"), "apis" -> array(routes
      .groupBy(_.describeFor(basePath))
      .map { case (path, routesForPath) => obj("path" -> string(path), "operations" -> array(routesForPath.map(render(_)._2))) }
      .toSeq: _*)))
  }

  override def badRequest(badParameters: Seq[ExtractionError]): Response = JsonErrorResponseRenderer.badRequest(badParameters)

  override def notFound(request: Request): Response = JsonErrorResponseRenderer.notFound()
}

object Swagger1dot1Json {
  def apply() = new Swagger1dot1Json()
} 
Example 81
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 82
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 83
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 84
Source File: CompositeTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.parameters

import com.twitter.finagle.http.Method.Get
import com.twitter.finagle.http.Request
import io.fintrospect.RequestBuilder
import io.fintrospect.util.{Extracted, Extraction, ExtractionError, ExtractionFailed}
import org.scalatest.{FunSpec, Matchers}

class CompositeTest extends FunSpec with Matchers {

  case class FooBar(foo: String, bar: Int)

  object FooBar extends Composite[FooBar] {
    val foo = add(Query.required.string("foo"))
    val bar = add(Query.required.int("bar"))

    override def -->(foobar: FooBar): Iterable[Binding] = (foo --> foobar.foo) ++ (bar --> foobar.bar)

    override def <--?(req: Request): Extraction[FooBar] = {
      for {
        foo <- foo <--? req
        bar <- bar <--? req
      } yield FooBar(foo, bar)
    }
  }

  describe("Composite") {
    it("extraction from request succeeds") {
      FooBar <--? Request("?foo=foo&bar=123") shouldBe Extracted(FooBar("foo", 123))
    }
    it("extraction from request for invalid field fails") {
      FooBar <--? Request("?foo=foo") shouldBe ExtractionFailed(ExtractionError.Missing(FooBar.bar))
    }
    it("reports all fields") {
      FooBar.toSeq shouldBe Seq(FooBar.foo, FooBar.bar)
    }
    it("supports binding") {
      val req = (FooBar --> FooBar("foo", 123)).foldLeft(RequestBuilder(Get)) { (requestBuild, next) => next(requestBuild) }.build()
      req.uri shouldBe "/?foo=foo&bar=123"
    }
  }
} 
Example 85
Source File: RebindableTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.parameters

import com.twitter.finagle.http.Method.Get
import com.twitter.finagle.http.Request
import io.fintrospect.RequestBuilder
import org.scalatest.{FunSpec, Matchers}

class RebindableTest extends FunSpec with Matchers {

  describe("Rebinding") {
    describe("Mandatory") {
      it("can rebind") {
        val inRequest = Request()
        inRequest.headerMap.add("field", "123")
        val bindings = Header.required.int("field") <-> inRequest
        val outRequest = bindings.foldLeft(RequestBuilder(Get)) { (requestBuild, next) => next(requestBuild) }.build()
        outRequest.headerMap("field") shouldBe "123"
      }
    }

    describe("Optional") {
      it("can rebind present value") {
        val inRequest = Request()
        inRequest.headerMap.add("field", "123")
        val bindings = Header.optional.int("field") <-> inRequest
        val outRequest = bindings.foldLeft(RequestBuilder(Get)) { (requestBuild, next) => next(requestBuild) }.build()
        outRequest.headerMap("field") shouldBe "123"
      }

      it("does not rebind missing value") {
        val inRequest = Request()
        val bindings = Header.optional.int("field") <-> inRequest
        val outRequest = bindings.foldLeft(RequestBuilder(Get)) { (requestBuild, next) => next(requestBuild) }.build()
        outRequest.headerMap.get("field") shouldBe None
      }
    }
  }
} 
Example 86
Source File: ExtractedRouteRequestTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.parameters

import com.twitter.finagle.http.Request
import io.fintrospect.util.ExtractionError.Missing
import io.fintrospect.util.{Extracted, ExtractionFailed}
import org.scalatest.{FunSpec, Matchers}



class ExtractedRouteRequestTest extends FunSpec with Matchers {

  describe("ExtractedRouteRequest") {
    it("uses pre-extracted value if present") {
      val bob = FormField.required.string("bob")
      val body = Body.form(bob)
      ExtractedRouteRequest(Request(), Map(body -> Extracted(Form()))).get(body, (_: Request) => ExtractionFailed(Missing(bob))) shouldBe Extracted(Form())
    }

    it("converts missing parameter to Extraction failed") {
      val bob = FormField.required.string("bob")
      val bill = FormField.required.string("bill")
      val body = Body.form(bob, bill)
      ExtractedRouteRequest(Request(), Map()).get(body, body.extract) shouldBe ExtractionFailed(Seq(Missing(bob), Missing(bill)))
    }
  }

} 
Example 87
Source File: FormTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.parameters

import com.twitter.finagle.http.Request
import io.fintrospect.util.ExtractionError.Missing
import io.fintrospect.util.{Extracted, ExtractionFailed}
import org.scalatest._

class FormTest extends FunSpec with Matchers {

  private val field1 = FormField.required.string("field1")
  private val field2 = FormField.required.string("field2")
  private val field3 = FormField.required.string("field3")
  private val field4 = FormField.required.string("field4")
  private val field5 = FormField.required.string("field5")
  private val field6 = FormField.required.string("field6")

  private val formSpec = Body.form(field1, field2, field3, field4, field5, field6)

  describe("construction") {
    it("from a set of bindings") {
      Form(Seq(new FormFieldBinding(field1, "value"))).fields.toSeq shouldBe Seq(("field1", Seq("value")))
    }
  }

  describe("multiple parameter retrieval") {
    val formInstance = Form(field1 --> "value1", field2 --> "value2", field3 --> "value3", field4 --> "value4", field5 --> "value5", field6 --> "value6")
    it("1 binding") {
      formInstance <-- field1 shouldBe "value1"
      formInstance <-- (field1, field2) shouldBe("value1", "value2")
      formInstance <-- (field1, field2, field3) shouldBe("value1", "value2", "value3")
      formInstance <-- (field1, field2, field3, field4) shouldBe("value1", "value2", "value3", "value4")
      formInstance <-- (field1, field2, field3, field4, field5) shouldBe("value1", "value2", "value3", "value4", "value5")
      formInstance <-- (field1, field2, field3, field4, field5, field6) shouldBe("value1", "value2", "value3", "value4", "value5", "value6")
    }
  }

  describe("retrieval") {
    it("handles empty form - optional") {
      val optional = FormField.optional.string("field1")
      Body.form(optional).extract(Request()) shouldBe Extracted(Form())
      (Body.form(optional) <-- Request()) shouldBe Form()
    }

    it("handles empty form - required") {
      formSpec.extract(Request()) shouldBe ExtractionFailed(Seq(field1, field2, field3, field4, field5, field6).map(f => Missing(f)))
    }
  }
} 
Example 88
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 89
Source File: ModuleTest.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.Method.Get
import com.twitter.finagle.http.path.Path
import com.twitter.finagle.http.{Method, Request, Response, Status}
import com.twitter.util.Await.result
import io.fintrospect.util.Echo
import io.fintrospect.util.HttpRequestResponseUtil.statusAndContentFrom
import org.scalatest.{FunSpec, Matchers}

class ModuleTest extends FunSpec with Matchers {

  describe("Module") {
    it("when it matches it responds as expected") {
      val response = statusAndContentFrom(result(routingWhichMatches((Get, Path("/someUrl")))(Request("/someUrl?field=hello"))))
      response._1 shouldBe Status.Ok
      response._2 should include("/someUrl?field=hello")
    }
    it("no match responds with default 404") {
      val response = result(routingWhichMatches((Get, Path("/someUrl")))(Request("/notMyService")))
      response.status shouldBe Status.NotFound
    }
  }

  private def routingWhichMatches(methodAndPath: (Method, Path)): Service[Request, Response] = {
    Module.toService(new PartialFunction[(Method, Path), Service[Request, Response]] {
      override def isDefinedAt(x: (Method, Path)): Boolean = x === methodAndPath

      override def apply(v1: (Method, Path)): Service[Request, Response] = Echo()
    })
  }
} 
Example 90
Source File: ContractProxyModuleTest.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.Method.Get
import com.twitter.finagle.http.Request
import com.twitter.util.Await
import io.fintrospect.formats.PlainText.ResponseBuilder._
import io.fintrospect.parameters.Query
import org.scalatest.{FunSpec, Matchers}

object TestContract extends Contract {

  object Endpoint extends ContractEndpoint {
    val query = Query.required.string("query")
    override val route = RouteSpec().taking(query).at(Get) / "hello"
  }

}

class ContractProxyModuleTest extends FunSpec with Matchers {

  describe("ContractProxyModule") {
    it("cretes service which proxies requests to the underlying service") {
      val svc = Service.mk { req: Request => Ok(TestContract.Endpoint.query <-- req) }
      Await.result(ContractProxyModule("remote", svc, TestContract).toService(Request("/hello?query=value"))).contentString shouldBe "value"
    }
  }
} 
Example 91
Source File: RenderViewTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.templating

import com.twitter.finagle.Service
import com.twitter.finagle.http.{Request, Status}
import com.twitter.io.Bufs
import com.twitter.util.Await.result
import io.fintrospect.formats.Html
import io.fintrospect.templating.View.Redirect
import org.scalatest.{FunSpec, Matchers}

class RenderViewTest extends FunSpec with Matchers {

  describe("RenderView") {
    val renderView = new RenderView(Html.ResponseBuilder, new TemplateRenderer {
      override def toBuf(view: View) = Bufs.utf8Buf(view.template)
    })

    it("creates a standard View") {
      val response = result(renderView(Request(), Service.const(OnClasspath(Nil))))
      response.status shouldBe Status.Ok
      response.contentString shouldBe "io/fintrospect/templating/OnClasspath"
    }

    it("creates a standard View with an overridden status") {
      val response = result(renderView(Request(), Service.const(OnClasspath(Nil, Status.NotFound))))
      response.status shouldBe Status.NotFound
      response.contentString shouldBe "io/fintrospect/templating/OnClasspath"
    }

    it("creates redirect when passed a RenderView.Redirect") {
      val response = result(renderView(Request(), Service.const(Redirect("newLocation", Status.BadGateway))))
      response.status shouldBe Status.BadGateway
      response.headerMap("Location") shouldBe "newLocation"
    }
  }

} 
Example 92
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 93
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 94
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 95
Source File: HeapDumpTest.scala    From fintrospect   with Apache License 2.0 5 votes vote down vote up
package io.fintrospect.util

import java.time.Instant

import com.twitter.finagle.http.{Request, Status}
import com.twitter.util.Await
import org.scalatest.{FunSpec, Matchers}

class HeapDumpTest extends FunSpec with Matchers {

  describe("HeapDump") {
    it("creates the correct heapdump file") {
      val clock = TestClocks.fixed(Instant.ofEpochMilli(0))
      val response = Await.result(new HeapDump("bob", clock).apply(Request()))
      response.status shouldBe Status.Ok
      response.headerMap("Content-disposition").startsWith("inline; filename=\"heapdump-bob-1970-01-01") shouldBe true
      response.contentType shouldBe Some("application/x-heap-dump;charset=utf-8")
    }
  }
} 
Example 96
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 97
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 98
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 99
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 100
Source File: MicroService.scala    From finagle-microservice-sample   with Apache License 2.0 5 votes vote down vote up
import java.net.InetSocketAddress

import com.twitter.finagle.builder.ServerBuilder
import com.twitter.finagle.http.{Http, Request, RichHttp}
import com.twitter.finagle.stats.MetricsExporter
import reports.FakeReportProcessor

object MicroService extends App {

  val loginService = new AlwaysValidLoginService()
  val reportProcessor = new FakeReportProcessor()
  val authenticateUser = new AuthenticationFilter(loginService)
  val processReport = new ProcessReportHandler(reportProcessor)
  val metricsExporter = new MetricsExporter()

  //setup service chain
  val serviceChain = authenticateUser andThen processReport

  //HTTP endpoint
  val socketAddress = new InetSocketAddress(8080)
  val server = ServerBuilder()
    .codec(new RichHttp[Request](Http()))
    .bindTo(socketAddress)
    .name("HTTP endpoint")
    .build(serviceChain)

  println("microservice started")
} 
Example 101
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 102
Source File: SwaggerController.scala    From swagger-finatra   with Apache License 2.0 5 votes vote down vote up
package com.github.xiaodongw.swagger.finatra

import com.twitter.finagle.http.Request
import com.twitter.finatra.http.Controller
import com.twitter.finatra.response.Mustache
import io.swagger.models.Swagger
import io.swagger.util.Json

class SwaggerController(docPath: String = "/api-docs", swagger: Swagger) extends Controller {
  get(s"${docPath}/model") { request: Request =>
    response.ok.body(Json.mapper.writeValueAsString(swagger))
      .contentType("application/json").toFuture
  }

  get(s"${docPath}/ui") { request: Request =>
    response.temporaryRedirect
        .location(s"/webjars/swagger-ui/2.2.8/index.html?url=${docPath}/model")
  }
} 
Example 103
Source File: Models.scala    From swagger-finatra   with Apache License 2.0 5 votes vote down vote up
package com.github.xiaodongw.swagger.finatra

import com.twitter.finagle.http.Request
import com.twitter.finatra.request.{QueryParam, RouteParam}
import io.swagger.annotations.{ApiModel, ApiModelProperty}
import javax.inject.Inject
import org.joda.time.{DateTime, LocalDate}

@ApiModel(value="AddressModel", description="Sample address model for documentation")
case class Address(street: String, zip: String)

case class Student(firstName: String, lastName: String, gender: Gender, birthday: LocalDate, grade: Int, address: Option[Address])

case class StudentWithRoute(
  @RouteParam
  @ApiModelProperty(name = "student_id", value = "Id of the student")
  id: String,
  @Inject request: Request,
  firstName: String,
  lastName: String,
  gender: Gender,
  birthday: LocalDate,
  grade: Int,
  emails: Array[String],
  address: Option[Address]
)

case class StringWithRequest(
  @Inject request: Request,
  firstName: String
)

object CourseType extends Enumeration {
  val LEC, LAB = Value
}

case class Course(time: DateTime,
                  name: String,
                  @ApiModelProperty(required = false, example = "[math,stem]")
                  tags: Seq[String],
                  @ApiModelProperty(dataType = "string", allowableValues = "LEC,LAB")
                  typ: CourseType.Value,
                  @ApiModelProperty(readOnly = true)
                  capacity: Int,
                  @ApiModelProperty(dataType = "double", required = true)
                  cost: BigDecimal) 
Example 104
Source File: InstanceMetadata.scala    From graphcool-framework   with Apache License 2.0 5 votes vote down vote up
package cool.graph.metrics

import com.twitter.finagle
import com.twitter.finagle.http.{Method, Request, Response}
import cool.graph.metrics.Utils._

import scala.concurrent.Future
import com.twitter.conversions.time._
import com.twitter.finagle.service.Backoff

object InstanceMetadata {
  import scala.concurrent.ExecutionContext.Implicits.global

  val service = finagle.Http.client.withRetryBackoff(Backoff.const(5.seconds)).withRequestTimeout(15.seconds).newService("169.254.169.254:80")

  
  private def fetch(path: String): Future[String] = {
    val request       = Request(Method.Get, path)
    val requestFuture = service(request).asScala

    requestFuture.onFailure({
      case e => throw MetricsError(s"Error while fetching request ${request.uri}: $e")
    })

    requestFuture.map { (response: Response) =>
      response.status match {
        case x if x.code >= 200 && x.code < 300 =>
          val ip = response.contentString
          println(s"[Metrics] Request ${request.uri} result: $ip")
          ip

        case _ =>
          throw MetricsError(s"Unable to retrieve EC2 metadata (${request.uri}) - ${response.status} | ${response.contentString}")
      }
    }
  }
} 
Example 105
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 106
Source File: StackParams.scala    From finch-101   with Apache License 2.0 5 votes vote down vote up
package i.f.workshop.finagle

import com.twitter.finagle.param
import com.twitter.finagle.http.{Response, Request}
import com.twitter.finagle.service.TimeoutFilter
import com.twitter.finagle.transport.Transport
import com.twitter.finagle.{Http, Service}
import com.twitter.util._
import com.twitter.conversions.time._

object StackParams extends App {

  implicit val timer: Timer = new JavaTimer()
  val service: Service[Request, Response] = new Service[Request, Response] {
    def apply(req: Request): Future[Response] =
      Future.sleep(5.seconds).map(_ => Response())
  }

  val monitor: Monitor = new Monitor {
    def handle(exc: Throwable): Boolean = {
      println(exc)

      false
    }
  }

  Await.ready(Http.server
    .configured(TimeoutFilter.Param(1.seconds))
    .configured(Transport.Verbose(true))
    .configured(param.Monitor(monitor))
    .serve(":8081", service)
  )
} 
Example 107
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 108
Source File: HttpRequest.scala    From cosmos   with Apache License 2.0 5 votes vote down vote up
package com.mesosphere.cosmos.http

import com.mesosphere.http.MediaType
import com.mesosphere.util.UrlSchemeHeader
import com.twitter.finagle.http.Fields
import com.twitter.finagle.http.Method
import com.twitter.finagle.http.Request
import com.twitter.io.Buf
import io.circe.Encoder
import io.circe.syntax._
import io.finch.Input

final case class HttpRequest(
  path: RpcPath,
  headers: Map[String, String],
  method: HttpRequestMethod
)

object HttpRequest {

  def collectHeaders(entries: (String, Option[String])*): Map[String, String] = {
    entries
      .flatMap { case (key, value) => value.map(key -> _) }
      .toMap
  }

  def get(path: RpcPath, accept: MediaType): HttpRequest = {
    get(path, toHeader(accept))
  }

  def get(path: RpcPath, accept: Option[String]): HttpRequest = {
    HttpRequest(path, collectHeaders(Fields.Accept -> accept), Get())
  }

  def post[A](
    path: RpcPath,
    body: A,
    contentType: MediaType,
    accept: MediaType
  )(implicit encoder: Encoder[A]): HttpRequest = {
    post(path, body.asJson.noSpaces, toHeader(contentType), toHeader(accept))
  }

  def post(
    path: RpcPath,
    body: String,
    contentType: Option[String],
    accept: Option[String]
  ): HttpRequest = {
    val headers = collectHeaders(
      Fields.Accept -> accept,
      Fields.ContentType -> contentType
    )
    HttpRequest(path, headers, Post(Buf.Utf8(body)))
  }

  def post(
    path: RpcPath,
    body: Buf,
    contentType: MediaType,
    accept: MediaType,
    host: String,
    urlScheme: String
  ): HttpRequest = {
    val headers = collectHeaders(
      Fields.Accept -> toHeader(accept),
      Fields.ContentType -> toHeader(contentType),
      Fields.Host -> Some(host),
      UrlSchemeHeader -> Some(urlScheme)
    )
    HttpRequest(path, headers, Post(body))
  }

  def toFinagle(cosmosRequest: HttpRequest): Request = {
    val finagleRequest = cosmosRequest.method match {
      case Get(params @ _*) =>
        Request(cosmosRequest.path.path, params: _*)
      case Post(buf) =>
        val req = Request(Method.Post, cosmosRequest.path.path)
        req.content = buf
        req.contentLength = buf.length.toLong
        req
    }

    finagleRequest.headerMap ++= cosmosRequest.headers
    finagleRequest
  }

  def toFinchInput(cosmosRequest: HttpRequest): Input = {
    val finagleRequest = toFinagle(cosmosRequest)

    Input(
      finagleRequest,
      finagleRequest.path.split("/")
    )
  }

  def toHeader(mediaType: MediaType): Option[String] = Some(mediaType.show)

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

import _root_.io.circe.Json
import com.mesosphere.cosmos.http.Authorization
import com.mesosphere.cosmos.http.RequestSession
import com.mesosphere.cosmos.test.TestUtil
import com.mesosphere.http.OriginHostScheme
import io.lemonlabs.uri.Uri
import com.twitter.finagle.http.Request
import org.scalatest.FreeSpec
import org.scalatest.Inside

final class ServiceClientSpec extends FreeSpec with Inside {

  import ServiceClientSpec._

  "A ServiceClient" - {
    "supports an optional AuthorizationHeaderName request header" - {
      "so that Cosmos can interact with security-enabled AdminRouters" - {
        val testClient = new AuthorizationTestClient()
        "header not provided" - {

          implicit val session = TestUtil.Anonymous

          "with baseRequestBuilder()" in {
            val requestBuilder = testClient.baseRequestBuilder(Uri.parse("/foo/bar/baz"))
            assert(!requestBuilder.buildGet.headerMap.contains(AuthorizationHeaderName))
          }
          "with get()" in {
            assert(!testClient.testGet.headerMap.contains(AuthorizationHeaderName))
          }
          "with post()" in {
            assert(!testClient.testPost.headerMap.contains(AuthorizationHeaderName))
          }
          "with postForm()" in {
            assert(!testClient.testPostForm.headerMap.contains(AuthorizationHeaderName))
          }
          "with delete()" in {
            assert(!testClient.testDelete.headerMap.contains(AuthorizationHeaderName))
          }
        }

        "header provided" - {

          implicit val session = RequestSession(
            Some(Authorization("credentials")),
            OriginHostScheme("localhost", OriginHostScheme.Scheme.http)
          )

          "with baseRequestBuilder()" in {
            val requestBuilder = testClient.baseRequestBuilder(Uri.parse("/foo/bar/baz"))
            val headerOpt = requestBuilder.buildDelete.headerMap.get(AuthorizationHeaderName)
            inside(headerOpt) { case Some(header) => assertResult("credentials")(header) }
          }
          "with get()" in {
            val headerOpt = testClient.testGet.headerMap.get(AuthorizationHeaderName)
            inside (headerOpt) { case Some(header) => assertResult("credentials")(header) }
          }
          "with post()" in {
            val headerOpt = testClient.testPost.headerMap.get(AuthorizationHeaderName)
            inside (headerOpt) { case Some(header) => assertResult("credentials")(header) }
          }
          "with postForm()" in {
            val headerOpt = testClient.testPostForm.headerMap.get(AuthorizationHeaderName)
            inside (headerOpt) { case Some(header) => assertResult("credentials")(header) }
          }
          "with delete()" in {
            val headerOpt = testClient.testDelete.headerMap.get(AuthorizationHeaderName)
            inside (headerOpt) { case Some(header) => assertResult("credentials")(header) }
          }
        }
      }
    }
  }

}

object ServiceClientSpec {
  private[cosmos] val PathUri: Uri = Uri.parse("/foo/bar/baz")
  private val AuthorizationHeaderName: String = "Authorization"
}

private final class AuthorizationTestClient
  extends ServiceClient(Uri.parse("http://example.com")) {

  def testGet(implicit session: RequestSession): Request = get(ServiceClientSpec.PathUri)
  def testPost(implicit session: RequestSession): Request = post(ServiceClientSpec.PathUri, Json.Null)
  def testPostForm(implicit session: RequestSession): Request = postForm(ServiceClientSpec.PathUri, "")
  def testDelete(implicit session: RequestSession): Request = delete(ServiceClientSpec.PathUri)

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

import java.util.concurrent.atomic.AtomicInteger

import ai.diffy.IsotopeSdkModule.IsotopeClient
import ai.diffy.proxy.Settings
import com.twitter.finagle.Http
import com.twitter.finagle.http.Request
import com.twitter.finatra.http.Controller
import javax.inject.Inject

class Frontend @Inject()(settings: Settings, isotopeClient: IsotopeClient) extends Controller {

  case class Dashboard(
    serviceName: String,
    serviceClass: String,
    apiRoot: String,
    excludeNoise: Boolean,
    relativeThreshold: Double,
    absoluteThreshold: Double,
    isotopeReason: String,
    hasIsotope: Boolean = false)

  val reasons = Seq(
    "Do you want to compare side effects like logs and downstream interactions?",
    "Do you want to save and share this comparison?",
    "Do you want to organize all your comparisons across all your services and teams in one place?",
    "Do you want to download sampled traffic?"
  )
  val reasonIndex = new AtomicInteger(0)
  get("/") { req: Request =>
    response.ok.view(
      "dashboard.mustache",
      Dashboard(
        settings.serviceName,
        settings.serviceClass,
        settings.apiRoot,
        req.params.getBooleanOrElse("exclude_noise", false),
        settings.relativeThreshold,
        settings.absoluteThreshold,
        reasons(reasonIndex.getAndIncrement() % reasons.length),
        isotopeClient.isConcrete
      )
    )
  }

  get("/css/:*") { request: Request =>
    response.ok.file(request.path)
  }

  get("/scripts/:*") { request: Request =>
    response.ok.file(request.path)
  }
} 
Example 111
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 112
Source File: ClientService.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.http.{Request, Response}
import com.twitter.finagle.thrift.ThriftClientRequest
import com.twitter.finagle.{Addr, Name, Service}
import com.twitter.util.{Time, Var}

trait ClientService[Req, Rep] {
  val client: Service[Req, Rep]
}

case class ThriftService(
    override val client: Service[ThriftClientRequest, Array[Byte]],
    path: Name)
  extends ClientService[ThriftClientRequest, Array[Byte]]
{
  var members: Int = 0
  var serversetValid = false
  var changedAt: Option[Time] = None
  var resetAt: Option[Time] = None
  var changeCount: Int = 0

  val boundServerset: Option[Var[Addr]] =
    path match {
      case Name.Bound(addr) =>
        serversetValid = true
        Some(addr)
      case _ =>
        serversetValid = false
        None
    }

  boundServerset foreach {
    _.changes.respond {
      case Addr.Bound(addrs, _) =>
        serversetValid = true
        sizeChange(addrs.size)
      case Addr.Failed(_) | Addr.Neg =>
        serversetValid = false
        sizeChange(0)
      case Addr.Pending => ()
    }
  }

  private[this] def sizeChange(size: Int) {
    changeCount += 1
    if (changeCount > 1) {
      changedAt = Some(Time.now)
      if (members == 0) {
        resetAt = Some(Time.now)
      }
    }
    members = size
  }
}

case class HttpService(
  override val client: Service[Request, Response])
extends ClientService[Request, Response] 
Example 113
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 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: FinchTemplateApi.scala    From finch-template   with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
package com.redbubble.finchtemplate

import com.redbubble.finchtemplate.api.v1
import com.redbubble.finchtemplate.api.v1.FinchTemplateErrorHandler.apiErrorHandler
import com.redbubble.finchtemplate.api.v1.{FinchTemplateErrorHandler, ResponseEncoders}
import com.redbubble.finchtemplate.util.config.Config._
import com.redbubble.finchtemplate.util.config.{Development, Environment, Test}
import com.redbubble.finchtemplate.util.metrics.Metrics._
import com.redbubble.hawk.HawkAuthenticateRequestFilter
import com.redbubble.util.http.filter.{ExceptionFilter, HttpBasicAuthFilter, RequestLoggingFilter, RoutingMetricsFilter}
import com.twitter.finagle.Service
import com.twitter.finagle.http.{Request, Response}
import io.finch.Application

object ApiAuthFilter extends HawkAuthenticateRequestFilter(apiAuthenticationCredentials, whitelistedAuthPaths, serverMetrics)

object BasicAuthFilter extends HttpBasicAuthFilter(s"$systemName Security", basicAuthCredentials, basicAuthPaths, serverMetrics)

object UnhandledExceptionsFilter extends ExceptionFilter(ResponseEncoders.throwableEncode, FinchTemplateErrorHandler, serverMetrics)

object RouteMetricsFilter extends RoutingMetricsFilter(serverMetrics)

object FinchTemplateApi extends ResponseEncoders {
  private val api = v1.api

  def apiService: Service[Request, Response] = filters andThen api.handle(apiErrorHandler).toServiceAs[Application.Json]

  private def filters = {
    val baseFilters = RequestLoggingFilter andThen UnhandledExceptionsFilter andThen RouteMetricsFilter
    Environment.env match {
      case Development => baseFilters
      case Test => baseFilters
      case _ => baseFilters andThen ApiAuthFilter andThen BasicAuthFilter
    }
  }
} 
Example 116
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 117
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 118
Source File: ZIOConvertService.scala    From typed-schema   with Apache License 2.0 5 votes vote down vote up
package ru.tinkoff.tschema.finagle.zioRouting
package impl

import com.twitter
import com.twitter.finagle.Service
import com.twitter.finagle.http.Request
import ru.tinkoff.tschema.finagle.ConvertService
import zio.{UIO, ZIO}

private[finagle] class ZIOConvertService[R, E] extends ConvertService[ZIOHttp[R, E, *]] {
  def convertService[A](svc: Service[Request, A]): ZIOHttp[R, E, A] =
    ZIO.accessM { r =>
      ZIO.effectAsyncInterrupt[ZioRouting[R], Fail[E], A] { cb =>
        val fut = svc(r.request).respond {
          case twitter.util.Return(a) => cb(ZIO.succeed(a))
          case twitter.util.Throw(ex) => cb(ZIO.die(ex))
        }

        Left(UIO(fut.raise(new InterruptedException)))
      }
    }
} 
Example 119
Source File: ZiosConvertService.scala    From typed-schema   with Apache License 2.0 5 votes vote down vote up
package ru.tinkoff.tschema.finagle.zioRouting
package impl

import com.twitter
import com.twitter.finagle.Service
import com.twitter.finagle.http.Request
import ru.tinkoff.tschema.finagle.ConvertService
import zio.{UIO, ZIO}

private[finagle] class ZiosConvertService[R, E] extends ConvertService[ZIOH[R, E, *]] {
  def convertService[A](svc: Service[Request, A]): ZIOH[R, E, A] =
    ZIO.accessM { r =>
      ZIO.effectAsyncInterrupt[R with HasRouting, Fail[E], A] { cb =>
        val fut = svc(r.get.request).respond {
          case twitter.util.Return(a) => cb(ZIO.succeed(a))
          case twitter.util.Throw(ex) => cb(ZIO.die(ex))
        }

        Left(UIO(fut.raise(new InterruptedException)))
      }
    }
} 
Example 120
Source File: ParamDirectives.scala    From typed-schema   with Apache License 2.0 5 votes vote down vote up
package ru.tinkoff.tschema
package finagle

import cats.Monad
import cats.instances.list._
import cats.syntax.applicative._
import cats.syntax.flatMap._
import com.twitter.finagle.http.{Request, Response}
import ru.tinkoff.tschema.param._
import shapeless._
import shapeless.labelled.{FieldType, field}

trait ParamDirectives[S <: ParamSource] {
  def source: S
  def getByName[F[_]: Routed: Monad, A](name: String, fa: Option[CharSequence] => F[A]): F[A]

  def notFound(name: String): Rejection                 = Rejection.missingParam(name, source)
  def malformed(name: String, error: String): Rejection = Rejection.malformedParam(name, error, source)

  def singleRejection(name: String, error: SingleParamError): Rejection =
    error match {
      case MissingParamError    => notFound(name)
      case ParseParamError(err) => malformed(name, err)
    }

  def errorReject[F[_], A](name: String, error: ParamError)(implicit routed: Routed[F]): F[A] =
    error match {
      case single: SingleParamError => routed.reject(singleRejection(name, single))
      case MultiParamError(vals)    =>
        Routed.rejectMany(vals.map { case (field, err) => singleRejection(field, err) }.toSeq: _*)
    }

  def direct[F[_]: Routed: Monad, A, name, In <: HList](
      name: String,
      result: Param.Result[A],
      in: In,
      k: (FieldType[name, A] :: In) => F[Response]
  ): F[Response] =
    result.fold(errorReject[F, Response](name, _), a => k(field[name](a) :: in))

  def provideOrReject[F[_]: Routed: Monad, A](name: String, result: Param.Result[A]): F[A] =
    result.fold(errorReject[F, A](name, _), _.pure[F])
}

abstract class ParamDirectivesSimple[S <: ParamSource](val source: S) extends ParamDirectives[S] {
  def getFromRequest(name: String)(req: Request): Option[CharSequence]

  def getByName[F[_]: Routed: Monad, A](name: String, f: Option[CharSequence] => F[A]): F[A] =
    Routed.request.flatMap(req => f(getFromRequest(name)(req)))
}

object ParamDirectives {
  def apply[S <: ParamSource](implicit dir: ParamDirectives[S]): ParamDirectives[S] = dir

  type TC[A <: ParamSource]  = ParamDirectives[A]
  type TCS[A <: ParamSource] = ParamDirectivesSimple[A]
  import ParamSource._

  implicit val queryParamDirective: TC[Query] = new TCS[Query](Query) {
    def getFromRequest(name: String)(req: Request): Option[String] = req.params.get(name)
  }

  implicit val cookieParamDirectives: TC[Cookie] = new TCS[Cookie](Cookie) {
    def getFromRequest(name: String)(req: Request): Option[String] = req.cookies.get(name).map(_.value)
  }

  implicit val pathParamDirectives: TC[ParamSource.Path] = new TC[ParamSource.Path] {
    def getByName[F[_]: Routed: Monad, A](name: String, fa: Option[CharSequence] => F[A]): F[A] = Routed.segment(fa)
    def source                                                                                  = ParamSource.Path
  }

  implicit val formDataParamDirectives: TC[Form] = new TCS[Form](Form) {
    def getFromRequest(name: String)(req: Request): Option[String] = req.params.get(name)
  }

  implicit val headerParamDirectives: TC[Header] = new TCS[Header](Header) {
    def getFromRequest(name: String)(req: Request): Option[String] = req.headerMap.get(name)
  }

} 
Example 121
Source File: VersionModule.scala    From typed-schema   with Apache License 2.0 5 votes vote down vote up
package ru.tinkoff.tschema
package example

import ru.tinkoff.tschema.finagle.Serve.Filter
import ru.tinkoff.tschema.finagle.{Rejection, Routed, Serve}
import ru.tinkoff.tschema.finagle.MkService
import ru.tinkoff.tschema.param.ParamSource.Query
import ru.tinkoff.tschema.swagger.{SwaggerMapper}
import ru.tinkoff.tschema.swagger.MkSwagger
import syntax._
import ru.tinkoff.tschema.typeDSL._
import shapeless.{HList, Witness}
import cats.syntax.flatMap._
import cats.syntax.applicative._
import cats.syntax.semigroupk._
import cats.syntax.order._
import cats.instances.string._
import cats.instances.string._
import ru.tinkoff.tschema.finagle.tethysInstances._
import cats.SemigroupK
import com.twitter.finagle.http.{Request, Response}
import ru.tinkoff.tschema.param.Param
import ru.tinkoff.tschema.common.Name
import Routed.{uriParam, reject}

object VersionModule extends ExampleModule {
  def api = tagPrefix("versioned") |> (
    (version("v1") |> get[String]) <>
      (version("v2") |> get[Map[String, Int]]) <>
      (version("v2.1") |> get[Vector[String]])
  )

  object service {
    def v1     = "Ololo"
    def v2     = Map("Olol" -> 0)
    def `v2.1` = Vector("Olo", "lo")
  }

  val route = MkService[Http](api)(service)
  val swag  = MkSwagger(api)
}

final class version[v] extends DSLAtom

object version {
  def wrongVersion(shouldBe: String, passed: String) =
    Rejection.malformedParam("version", s"passed version $passed shouldBe: $shouldBe", Query)

  def apply[v <: Singleton](v: Witness.Aux[v]): version[v] :> Key[v] = new :>

  implicit def versionServe[v: Name, In <: HList]: Filter[version[v], Http, In] =
    Serve.checkCont[version[v], Http, In] { cnt =>
      val shouldBe = Name[v].string

      Routed.checkPath[Http, Response](Name[v].string, cnt) <+>
        (uriParam[Http, String]("version").flatMap { s =>
          reject[Http, Unit](wrongVersion(shouldBe, s)).whenA(s =!= shouldBe)
        } *> cnt)
    }

  implicit def versionSwagger[v: Name]: SwaggerMapper[version[v]] = SwaggerMapper[Prefix[v]].as[version[v]]
} 
Example 122
Source File: VersionModule.scala    From typed-schema   with Apache License 2.0 5 votes vote down vote up
package ru.tinkoff.tschema.example

import ru.tinkoff.tschema.finagle.Serve.Filter
import ru.tinkoff.tschema.finagle.{MkService, Rejection, Routed, Serve}
import ru.tinkoff.tschema.param.ParamSource.Query
import ru.tinkoff.tschema.swagger.{SwaggerMapper, _}
import syntax._
import ru.tinkoff.tschema.typeDSL._
import shapeless.{HList, Witness}
import cats.syntax.flatMap._
import cats.syntax.applicative._
import cats.syntax.semigroupk._
import cats.syntax.order._
import cats.instances.string._
import ru.tinkoff.tschema.common.Name
import cats.instances.string._
import ru.tinkoff.tschema.finagle.tethysInstances._
import Routed.{reject, uriParam}
import cats.SemigroupK
import com.twitter.finagle.http.{Request, Response}
import ru.tinkoff.tschema.param.Param

object VersionModule extends ExampleModule {
  def api = tagPrefix('versioned) |> (
    (version('v1) |> get[String]) <>
      (version('v2) |> get[Map[String, Int]]) <>
      (version("v2.1") |> get[Vector[String]])
  )

  object service {
    def v1     = "Ololo"
    def v2     = Map("Olol" -> 0)
    def `v2.1` = Vector("Olo", "lo")
  }

  val route = MkService[Http](api)(service)
  val swag  = MkSwagger(api)
}

final class version[v] extends DSLAtom

object version {
  def wrongVersion(shouldBe: String, passed: String) =
    Rejection.malformedParam("version", s"passed version $passed shouldBe: $shouldBe", Query)

  def apply[v](v: Witness.Aux[v]): version[v] :> Key[v] = new version[v] :> key(v)

  implicit def versionServe[v: Name, In <: HList]: Filter[version[v], Http, In] =
    Serve.checkCont[version[v], Http, In] { cnt =>
      val shouldBe = Name[v].string

      Routed.checkPath[Http, Response](Name[v].string, cnt) <+>
        (uriParam[Http, String]("version").flatMap { s =>
          reject[Http, Unit](wrongVersion(shouldBe, s)).whenA(s =!= shouldBe)
        } >> cnt)
    }

  implicit def versionSwagger[v: Name]: SwaggerMapper[version[v]] = SwaggerMapper[Prefix[v]].as[version[v]]
} 
Example 123
Source File: MockAppSpec.scala    From peregrine   with Apache License 2.0 5 votes vote down vote up
package io.peregrine.test

import io.peregrine.{Controller, MockApp}
import com.twitter.finagle.http.Request
import org.jboss.netty.handler.codec.http.HttpMethod
import org.scalatest.{FlatSpec, Matchers}

import scala.collection.JavaConverters._

class MockAppSpec extends FlatSpec with Matchers {
  val server = MockApp(new Controller{})

  "#toByteArray" should "directly convert String to Array[Byte]" in {
    val value = "hello world"
    server.toByteArray(value).get should be(value.getBytes)
  }

  it should "also directly convert Array[Byte] to Array[Byte]" in {
    val value = "hello world".getBytes
    server.toByteArray(value).get should be(value)
  }

  it should "convert Map[String, String] to url-encoded form data" in {
    val value = Map("hello" -> "world")
    server.toByteArray(value).get should be("hello=world".getBytes)
  }

  it should "convert util.Map[String, String] to url-encoded form data" in {
    val value = Map("hello" -> "world").asJava
    server.toByteArray(value).get should be("hello=world".getBytes)
  }

  it should "convert null to None" in {
    server.toByteArray(null) should be(None)
  }

  it should "attempt to convert other objects to a json equivalent" in {
    val sample = Sample("matt", "[email protected]")
    server.toByteArray(sample).get should be( """{"name":"matt","email":"[email protected]"}""".getBytes)
  }

  "#buildRequest" should "apply body if present" in {
    val sample = Sample("matt", "[email protected]")

    // When
    val request: Request = server.buildRequest(HttpMethod.POST, "/", body = sample)

    // Then
    request.contentString should be(MockApp.mapper.writeValueAsString(sample))
  }

  it should "not allow both params AND a non-null body in the same request" in {
    val sample = Sample("matt", "[email protected]")
    a [RuntimeException] should be thrownBy {
      server.buildRequest(HttpMethod.POST, "/", params = Map("hello" -> "world"), body = sample)
    }
  }

  case class Sample(name: String, email: String)
} 
Example 124
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 125
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 126
Source File: FinatraDecodeInputsContext.scala    From tapir   with Apache License 2.0 5 votes vote down vote up
package sttp.tapir.server.finatra

import com.twitter.finagle.http.Request
import sttp.model.{Method, QueryParams}
import sttp.tapir.model.ServerRequest
import sttp.tapir.server.internal.DecodeInputsContext

class FinatraDecodeInputsContext(request: Request, pathConsumed: Int = 0) extends DecodeInputsContext {
  override def method: Method = Method(request.method.toString.toUpperCase)

  override def nextPathSegment: (Option[String], DecodeInputsContext) = {
    val path = request.path.drop(pathConsumed)
    val nextStart = path.dropWhile(_ == '/')
    val segment = nextStart.split("/", 2) match {
      case Array("")   => None
      case Array(s)    => Some(s)
      case Array(s, _) => Some(s)
    }
    val charactersConsumed = segment.map(_.length).getOrElse(0) + (path.length - nextStart.length)

    (segment, new FinatraDecodeInputsContext(request, pathConsumed + charactersConsumed))
  }
  override def header(name: String): List[String] = request.headerMap.getAll(name).toList
  override def headers: Seq[(String, String)] = request.headerMap.toList
  override def queryParameter(name: String): Seq[String] = request.params.getAll(name).toSeq
  override def queryParameters: QueryParams = QueryParams.fromMultiMap(request.params.toList.groupBy(_._1).mapValues(_.map(_._2)))
  override def bodyStream: Any = request.content
  override def serverRequest: ServerRequest = new FinatraServerRequest(request)
} 
Example 127
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(_ => ())
  }
} 
Example 128
Source File: package.scala    From featherbed   with Apache License 2.0 4 votes vote down vote up
package featherbed

import java.net.URL

import com.twitter.finagle.{Filter, Http}
import com.twitter.finagle.http.{Request, Response}
import org.scalamock.matchers.Matcher
import org.scalamock.scalatest.MockFactory

package object fixture {
  private[fixture] class MockClient (
    baseUrl: URL,
    filter: Filter[Request, Response, Request, Response]
  ) extends Client(baseUrl) {
    override def clientTransform(c: Http.Client) = c.filtered(filter)
  }

  trait ClientTest { self: MockFactory =>
    class TransportRequestMatcher(f: Request => Unit) extends Matcher[Any] {
      override def canEqual(x: Any) = x match {
        case x: Request => true
        case _ => false
      }
      override def safeEquals(that: Any): Boolean = that match {
        case x: Request => f(x); true
        case _ => false
      }
    }

    def request(f: Request => Unit): TransportRequestMatcher = new TransportRequestMatcher(f)

    def mockClient(url: String, filter: Filter[Request, Response, Request, Response]): Client =
      new MockClient(new URL(url), filter)
  }
}