Home

Awesome

<h1> <a href="http://fintrospect.io">Fintrospect</a>&nbsp;&nbsp;&nbsp; <a href="https://bintray.com/fintrospect/maven/fintrospect-core/_latestVersion"><img src="https://api.bintray.com/packages/fintrospect/maven/fintrospect-core/images/download.svg"/></a>&nbsp;&nbsp;&nbsp; <a href="https://travis-ci.org/daviddenton/fintrospect"><img src="https://travis-ci.org/daviddenton/fintrospect.svg?branch=master"/></a>&nbsp;&nbsp;&nbsp; <a href="https://coveralls.io/github/daviddenton/fintrospect?branch=master"><img src="https://coveralls.io/repos/daviddenton/fintrospect/badge.svg?branch=master"/></a>&nbsp;&nbsp;&nbsp; <a href="https://gitter.im/daviddenton/fintrospect"><img src="https://badges.gitter.im/daviddenton/fintrospect.svg"/></a>&nbsp;&nbsp;&nbsp; <a href="https://bintray.com/daviddenton/maven/fintrospect/view?source=watch"><img height="45" src="https://www.bintray.com/docs/images/bintray_badge_color.png"/></a>&nbsp;&nbsp;&nbsp; </h1>

Fintrospect is a Scala web-framework with an intelligent HTTP routing layer, based on the <a href="http://twitter.github.io/finagle/">Finagle</a> RPC framework from Twitter. Via a shared contract, it provides a simple way to implement fast webservice endpoints and HTTP clients which are:

Additionally, Fintrospect provides a number of mechanisms to leverage these routes:

Get it

Fintrospect is intentionally dependency-lite by design - other than Finagle, the core library itself only has a single non org.scala dependency. No dependency on Scalaz, Cats or Shapeless, so there are no compatibility headaches.

To activate the extension library features (JSON, templates etc), additional dependencies are required - please see <a href="http://fintrospect.io/installation">here</a> for details.

Add the following lines to build.sbt - the lib is hosted in Maven Central and JCenter:

resolvers += "JCenter" at "https://jcenter.bintray.com"
libraryDependencies += "io.fintrospect" %% "fintrospect-core" % "17.0.0"

See the code

See the <a href="https://github.com/daviddenton/fintrospect/tree/master/src/main/scala/examples">examples</a> or <a href="https://github.com/daviddenton/fintrospect/tree/master/src/test/scala/cookbook">cookbook</a> in this repo, or clone the <a href="http://github.com/daviddenton/fintrospect-example-app">full example application repo</a>.

Learn it

See the full user guide <a href="http://fintrospect.io/">here</a>, or read on for the tldr; example. :)

Server-side contracts

Adding Fintrospect routes to a Finagle HTTP server is simple. For this example, we'll imagine a Library application (see the example above for the full code) which will be rendering Swagger v2 documentation.

Define the endpoint

This example is quite contrived (and almost all the code is optional) but shows the kind of thing that can be done. Note the use of the example response object, which will be broken down to provide the JSON model for the Swagger documentation.

// Response building methods and implicit conversion from ResponseBuilder -> Future[Response] pulled in here
import io.fintrospect.formats.Argo.ResponseBuilder._
import io.fintrospect.formats.Argo.JsonFormat.array

class BookSearch(books: Books) {
  private val maxPages = Query.optional.int("maxPages", "max number of pages in book")
  private val minPages = FormField.optional.int("minPages", "min number of pages in book")
  private val titleTerm = FormField.required.string("term", "the part of the title to look for")
  private val form = Body.form(minPages, titleTerm)

  private def search() = Service.mk[Request, Response] { 
    request => {
      val requestForm = form <-- request
      Ok(array(
        books.search(
            (minPages <-- requestForm).getOrElse(MIN_VALUE), 
            (maxPages <-- request).getOrElse(MAX_VALUE),
            titleTerm <-- requestForm
        ).map(_.toJson)))
    }
  }

  val route = RouteSpec("search for books")
    .taking(maxPages)
    .body(form)
    .returning(Status.Ok -> "we found your book", array(Book("a book", "authorName", 99).toJson))
    .returning(Status.BadRequest -> "invalid request")
    .producing(ContentTypes.APPLICATION_JSON)
    .at(Method.Post) / "search" bindTo search
}

Define a module to live at http://{host}:8080/library

This module will have a single endpoint search:

val apiInfo = ApiInfo("Library Example", "1.0", Option("Simple description"))
val renderer = Swagger2dot0Json(apiInfo) 
val libraryModule = RouteModule(Root / "library", renderer)
    .withRoute(new BookSearch(new BookRepo()).route)
Http.serve(":8080", new HttpFilter(Cors.UnsafePermissivePolicy).andThen(libraryModule.toService)) 

View the generated documentation

The auto-generated documentation lives at the root of the module, so point the Swagger UI at http://{host}:8080/library to see it.

Client-side contracts

Declare the fields to be sent to the client service and then bind them to a remote service. This produces a simple function, which can then be called with the bindings for each parameter.

Since we can re-use the routes between client and server, we can easily create fake implementations of remote systems without having to redefine the contract. This means that marshalling of objects and values into/out of the HTTP messages can be reused.

  val theDate = Path.localDate("date")
  val gender = FormField.optional.string("gender")
  val body = Body.form(gender)

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

  val fakeServerRoute = sharedRouteSpec bindTo (dateFromPath => Service.mk[Request, Response] {
    request: Request => {
      // insert stub server implementation in here
      println("Form sent was " + (body <-- request))
      Ok(dateFromPath.toString)
    }
  })

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

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

  val theCall = client(
    body --> Form(gender --> "male"), 
    theDate --> LocalDate.of(2015, 1, 1)
  )

  println(Await.result(theCall))

Upgrading?

See the <a href="https://github.com/daviddenton/fintrospect/blob/master/CHANGELOG.md">changelog</a>.

Contributing

There are many ways in which you can contribute to the development of the library:

See the <a href="https://github.com/daviddenton/fintrospect/blob/master/CONTRIBUTING.md"/>contributor guide</a> for details.