/finatra

Fast, testable, Scala services built on Twitter-Server and Finagle

Primary LanguageScalaApache License 2.0Apache-2.0

Finatra

Fast, testable, Scala services built on Twitter-Server and Finagle

finatra logo

Build Status Test Coverage

Maven Central

Gitter

Features

  • Production use as Twitter’s HTTP framework
  • ~50 times faster than v1.6 in several benchmarks
  • Powerful feature and integration test support
  • Optional JSR-330 Dependency Injection using Google Guice
  • Jackson based JSON parsing supporting required fields, default values, and custom validations
  • Logback MDC integration with com.twitter.util.Local for contextual logging across futures

Presentations

Check out our list of recent presentations: Finatra Presentations

News

  • Finatra is now built against the latest Finagle v6.30.0 and Twitter Server v1.15.0 releases.
  • Please take a look at our new User Guide!
  • Keep up with the latest news here on our blog.

To get started we'll focus on building an HTTP API for posting and getting tweets:

Domain

case class TweetPostRequest(
  @Size(min = 1, max = 140) message: String,
  location: Option[TweetLocation],
  nsfw: Boolean = false)

case class TweetGetRequest(
  @RouteParam id: TweetId)

Then, let's create a Controller:

Controller

@Singleton
class TweetsController @Inject()(
  tweetsService: TweetsService)
  extends Controller {

  post("/tweet") { requestTweet: TweetPostRequest =>
    for {
      savedTweet <- tweetsService.save(requestTweet)
      responseTweet = TweetResponse.fromDomain(savedTweet)
    } yield {
      response
        .created(responseTweet)
        .location(responseTweet.id)
    }
  }

  get("/tweet/:id") { request: TweetGetRequest =>
    tweetsService.getResponseTweet(request.id)
  }
}

Next, let's create a server:

Server

class TwitterCloneServer extends HttpServer {
  override val modules = Seq(FirebaseHttpClientModule)

  override def configureHttp(router: HttpRouter): Unit = {
    router
      .filter[CommonFilters]
      .add[TweetsController]
  }
}

And finally, we can write a Feature Test:

Feature Test

class TwitterCloneFeatureTest extends FeatureTest with Mockito {

  override val server = new EmbeddedHttpServer(new TwitterCloneServer)

  @Bind val firebaseClient = smartMock[FirebaseClient]

  @Bind val idService = smartMock[IdService]

  "tweet creation" in {
    //Setup mocks
    idService.getId returns Future(StatusId("123"))

    val tweetResponse = TweetResponse(...)
    firebaseClient.put("/tweets/123.json", tweetResponse) returns Future.Unit
    firebaseClient.get("/tweets/123.json")(manifest[TweetResponse]) returns Future(Option(tweetResponse))

    //Assert tweet post
    val result = server.httpPost(
      path = "/tweet",
      postBody = """
        {
          "message": "Hello #FinagleCon",
          "location": {
            "lat": "37.7821120598956",
            "long": "-122.400612831116"
          },
          "nsfw": false
        }""",
      andExpect = Created,
      withJsonBody = """
        {
          "id": "123",
          "message": "Hello #FinagleCon",
          "location": {
            "lat": "37.7821120598956",
            "long": "-122.400612831116"
          },
          "nsfw": false
        }""")

    server.httpGetJson[TweetResponse](
      path = result.location.get,
      andExpect = Ok,
      withJsonBody = result.contentString)
  }

  "Post bad tweet" in {
    server.httpPost(
      path = "/tweet",
      postBody = """
        {
          "message": "",
          "location": {
            "lat": "9999"
          },
          "nsfw": "abc"
        }""",
      andExpect = BadRequest,
      withJsonBody = """
        {
          "errors" : [
            "message: size [0] is not between 1 and 140",
            "location.lat: [9999.0] is not between -85 and 85",
            "location.long: field is required",
            "nsfw: 'abc' is not a valid boolean"
          ]
        }
        """)
  }
}

Detailed Documentation

The Finatra project is composed of several libraries. You can find details in a project's README or see the User Guide for detailed information on building applications with Finatra.

Example Projects

For more detailed information see the README.md within each example project.

A barebones "Hello World" service.

A barebones service that is deployable to Heroku.

A url shortening example that is deployable to Heroku.

An example Twitter-like API for creating and retrieving Tweets.

A server used for benchmarking performance compared to a raw finagle-http service.

A proof-of-concept streaming JSON service.

Authors

A full list of contributors can be found on GitHub.

Follow @finatra on Twitter for updates.

License

Copyright 2015 Twitter, Inc.

Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0