Fast, testable Scala services inspired by Sinatra and powered by twitter-server
.
Finatra v2 slides from SFScala meetup
Documentation for prior versions can be found here.
- Production use as Twitter’s HTTP framework
- ~50 times faster than v1.6 in several benchmarks
- Powerful feature and integration test support
- 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
- Guice request scope integration with Futures
To get started we'll focus on building an HTTP API for posting and getting tweets:
case class PostedTweet(
@Size(min = 1, max = 140) message: String,
location: Option[Location],
sensitive: Boolean = false) {
case class GetTweet(
@RouteParam id: StatusId)
Then, let's create a Controller
:
@Singleton
class TweetsController @Inject()(
tweetsService: TweetsService)
extends Controller {
post("/tweet") { postedTweet: PostedTweet =>
tweetsService.save(postedTweet) map { savedTweet =>
response
.created(savedTweet)
.location(savedTweet.id)
}
}
get("/tweet/:id") { request: GetTweet =>
tweetsService.get(request.id)
}
}
Next, let's create a server:
class TwitterCloneServer extends HttpServer {
override val modules = Seq(FirebaseHttpClientModule)
override def configureHttp(router: HttpRouter): Unit = {
router
.register[StatusMessageBodyWriter]
.filter[CommonFilters]
.add[TweetsController]
}
}
And finally, we can write a Feature Test:
class TwitterCloneFeatureTest extends FeatureTest with Mockito {
override val server = new EmbeddedHttpServer(
twitterServer = new TwitterCloneServer {
override val overrideModules = Seq(integrationTestModule)
})
@Bind val firebaseClient = smartMock[FirebaseClient]
@Bind val idService = smartMock[IdService]
"tweet creation" in {
//Setup mocks
idService.getId returns Future(StatusId("123"))
val mockStatus = Status(
id = StatusId("123"),
text = "Hello #SFScala",
lat = Some(37.7821120598956),
long = Some(-122.400612831116),
sensitive = false)
firebaseClient.put("/statuses/123.json", mockStatus) returns Future.Unit
firebaseClient.get("/statuses/123.json")(manifest[Status]) returns Future(Option(mockStatus))
//Assert tweet post
val result = server.httpPost(
path = "/tweet",
postBody = """
{
"message": "Hello #SFScala",
"location": {
"lat": "37.7821120598956",
"long": "-122.400612831116"
},
"sensitive": false
}""",
andExpect = Created,
withJsonBody = """
{
"id": "123",
"message": "Hello #SFScala",
"location": {
"lat": "37.7821120598956",
"long": "-122.400612831116"
},
"sensitive": false
}""")
//Assert tweet get
server.httpGet(
path = result.location.get,
andExpect = Ok,
withJsonBody = result.contentString)
}
"Post bad tweet" in {
server.httpPost(
path = "/tweet",
postBody = """
{
"message": "",
"location": {
"lat": "9999"
},
"sensitive": "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 is a required field",
"sensitive's value 'abc' is not a valid boolean"
]
}
""")
}
}
We are publishing Scala 2.10 and 2.11 compatible libraries to Maven central. The Finatra project is currently split up into multiple components: (Twitter Inject and Finatra libraries).
Inject provides libraries for integrating twitter-server
and util-app
with Google Guice.
Finatra is a framework for easily building API services on top of Twitter’s Scala stack (twitter-server, finagle, twitter/util)
You can run the examples in finatra/examples using sbt, e.g., to run the finatra/examples/finatra-hello-world example,
$ sbt helloWorld/run
Or you can create an assembly and run the assembly,
$ sbt helloWorld/assembly
...
$ java -jar examples/finatra-hello-world/target/scala-2.11/finatra-hello-world-assembly-2.0.0.M3-SNAPSHOT.jar -http.port=:8888 -admin.port=:9990
- Steve Cosenza https://github.com/scosenza
- Christopher Coco https://github.com/cacoco
- Jason Carey https://github.com/jcarey03
- Eugene Ma https://github.com/edma2
A full list of contributors can be found on GitHub.
Follow @finatra on Twitter for updates.
Copyright 2015 Twitter, Inc.
Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0