Finatra: Fast, Testable, Scala Services. Steve Cosenza Finatra & Data API Tech

Similar documents
Microservices. Webservices with Scala (II) Microservices

Unit Testing & Testability

Transitioning from C# to Scala Using Apache Thrift. Twitter Finagle

Patterns and Best Practices for dynamic OSGi Applications

Scala, Your Next Programming Language

Scala Java Sparkle Monday, April 16, 2012

Patterns and Best Practices for Dynamic OSGi Applications

Big Modular Java with Guice

Enterprise Java TM Web Apps with Google Open Source Technology

Design patterns using Spring and Guice

Tools for Accessing REST APIs

WebTestClient. Version RELEASE

Using Swagger to tame HTTP/JSON interfaces. John Billings

Programming Language Impact on the Development of Distributed Systems

OSGi on the Server. Martin Lippert (it-agile GmbH)

JavaFX Codeeditors. Tom Schindl

Making most of Scala. Akka, Scala, Spray, Specs2; all in 50 minutes!

glu deployment automation platform July 2011 Yan Pujante in: blog:

Eclipse MicroProfile: Accelerating the adoption of Java Microservices

JVA-563. Developing RESTful Services in Java

ArcGIS Web Viewers and Templates. Andy Gup, Jim Barry

Spark, Shark and Spark Streaming Introduction

BaasBox. Open Source Backend as a Service. Otto Hylli

Downloading Tweet Streams and Parsing

Curriculum Vitae. Android Developer EDUCATION EDUCATION. Radioelectronics Electronic-optical machine building

2 years without Java or reload Android development with Kotlin.

THE VOEIS HIS GATEWAY. A REST Interface for HydroServer using ODM 1.1

Program Correctness and Efficiency. Chapter 2

Thrift specification - Remote Procedure Call

LUCITY REST API INTRODUCTION AND CORE CONCEPTS

From Java EE to Jakarta EE. A user experience

Кирилл Розов Android Developer

2 Years of. Real World FP at Scala Developer at

Who am I? Harlan Iverson. Programming enthusiast. Seeker of truth. Imperfect. I'll be wrong about some things. Please correct me if you can.

Christoph Doblander. Joint work with: Christoph Goebel, Hans-Arno Jacobsen

Combining Concurrency Abstractions

Don t give up on mocking

Evolution of an Apache Spark Architecture for Processing Game Data

Arun Gupta is a technology enthusiast, a passionate runner, and a community guy who works for Sun Microsystems. And this is his blog!

Spring Framework 5.0 Themes & Trends

Copyright 2014 Blue Net Corporation. All rights reserved

HOW WE MOVED FROM JAVA TO SCALA

Embedded type method, overriding, Error handling, Full-fledged web framework, 208 Function defer, 31 panic, 32 recover, 32 33

JVA-117A. Spring-MVC Web Applications

John M. Scarpaci. Summary. Professional Experience

Quick housekeeping Last Two Homeworks Extra Credit for demoing project prototypes Reminder about Project Deadlines/specifics Class on April 12th Resul

Spring Framework 5.0 on JDK 8 & 9

Silex and Twig. Jon Ginn

Kotlin for Android Developers

Selenium Testing Course Content

Croquet. William R. Speirs, Ph.D. Founder & CEO of Metrink

TIBCO Upgrade Project New Hub Message Processing Issue ReMCoWG Update

Tenable.io Container Security REST API. Last Revised: June 08, 2017

Accessibility. Adding features to support users with impaired vision, mobility, or hearing

Silhouette Documentation

JakartaEE and the road ahead An ASF View. Mark Struberg, RISE GmbH, Apache Software Foundation, INSO TU Wien

Ratpacked Notebook. Experience Ratpack with code snippets. Hubert Klein Ikkink. This book is for sale at

Scriptable Markdown pretty-printing with GraalVM

Node.js Vulnerabilities

Eclipse Scout. Release Notes. Scout Team. Version 7.0

TLCPowerTalk.com. Communication for Management Professionals. QCon London 2009 (c) 12 March by Peter Pilgrim 1.

Using Scala for building DSL s

A Tracing Technique for Understanding the Behavior of Large-Scale Distributed Systems

IGL S AND MOUNT NS. Taking AKKA to Production

CPL 2016, week 10. Clojure functional core. Oleg Batrashev. April 11, Institute of Computer Science, Tartu, Estonia

Signals Documentation

PROFILE OPTIONS FOR CONCURRENT MANAGER(CM)

Simplifying Asynchronous Code with

Kotlin for Android Developers

NODE.JS MOCK TEST NODE.JS MOCK TEST I

Macro #clojureconj

Distributed Systems. 25. Authentication Paul Krzyzanowski. Rutgers University. Fall 2018

Spoilt for Choice Which Integration Framework to choose? Mule ESB. Integration. Kai Wähner

Executive Summary. It is important for a Java Programmer to understand the power and limitations of concurrent programming in Java using threads.

CS November 2018

Philipp Wille. Beyond Scala s Standard Library

Semantic Analysis. Outline. The role of semantic analysis in a compiler. Scope. Types. Where we are. The Compiler Front-End

Inside Cloudbleed John Graham-Cumming

Scope & Symbol Table. l Most modern programming languages have some notion of scope.

GAVIN KING RED HAT CEYLON SWARM

Covers Play, Akka, and Reactive Streams. Manuel Bernhardt. FOREWORD BY James Roper SAMPLE CHAPTER MANNING

CS Programming I: Arrays

JVA-117E. Developing RESTful Services with Spring

Guice. Java DI Framework

TSAR A TimeSeries AggregatoR. Anirudh Todi TSAR

Software Project Seminar VII: Tools of the Craft. 23 march 2006 Jevgeni Kabanov

Scripting Languages in OSGi. Thursday, November 8, 12

web engineering introduction

Product Versioning and Back Support Policy

Scala, Lift and the Real Time Web

ITP 140 Mobile Technologies. Mobile Topics

CS410J: Advanced Java Programming

CS162 Week 1. Kyle Dewey. Friday, January 10, 14

PROCE55 Mobile: Web API App. Web API.

flask-jwt-simple Documentation

Test Isolation and Mocking

From Scala to Typesafe. Martin Odersky November 4 th 2011 Venture Ideas Symposium

Financial Services and Insurance. Get more out of your mobile campaigns

Getting Started with Gradle

Rich Web Applications in Server-side Java without. Plug-ins or JavaScript

Transcription:

Finatra: Fast, Testable, Scala Services Steve Cosenza Finatra & Data API Tech Lead @scosenza

+ = FINAGLE SINATRA FINATRA

Finagle is an extensible remote procedure call system for the JVM, used to construct high-concurrency servers. FINAGLE

FINAGLE

Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort. SINATRA

FINATRA A lightweight framework for building feature testable applications on Twitter s open source stack.

Finatra Project History Version 2 Declared Twitter s new HTTP Framework Version 1 Released on Github Version 2 M1 Released on Github Version 2 Released FEBRUARY 2012 March 2014 APRIL 2015 JUNE 2015 JULY 2015 SEPTEMBER 2015 Version 2 Released Internally at Twitter Version 2 M2 Released

Open Source - Apache 2 License

Feature Testing

FEATURE TEST A hybrid of an integration and component test enabling test-doubles and black-box/white-box assertions.

Feature Tests Create User Feature ApiFeatureTest ControllerIntegrationTest ControllerUnitTest UserDAOIntegrationTest UserDAOUnitTest IdServiceUnitTest UserValidatorUnitTest

In our test philosophy a feature test is something we must have It s a test that verifies that something we want to build works, and works as expected If you don t have a feature test, you might not even have a feature. Viktor Klang Akka Tech Lead Typesafe

Feature Test class PopularApiFeatureTest extends FeatureTest { val server = new EmbeddedHttpServer(new PopularApiServer) "Handle POST request" in { server.httppost( path = "/api/popular", postbody = """{ "start": "2015-09-01",... """, andexpect = Status.Ok, withjsonbody = """{ "start" : "2015-09-01T00:00:00Z", "end" : "2015-09-01T00:00:00Z", "tweets" : [... ] """)

Qualities Of A Good Test Fast Non-Flaky Catches Bugs Easy to maintain Easy to write

Real World Feature Tests

Dependency Injection Frameworks

BOILERPLATE The sections of code that have to be included in many places with little or no alteration

Dependency Injection Frameworks Eliminate Boilerplate Consistent Configuration Facilitate Testing

The main advantage that [Dependency Injection] brings is not realised until you start writing tests... once you move onto integration testing, this is where Play can start to realise the advantages of DI. James Roper (March 2015) Play Framework Tech Lead Typesafe

JSR-330 Lightweight dependency injection framework for Java brought to you by GUICE Google.

JSR-330 Dependency Injection @Singleton class PopularApiController @Inject()( userrepository: UserRepository, searchservice: SearchService, engagementservice: EngagementService) extends Controller { post("/api/popular") { request: PopularPostRequest =>...

Server Construction class PopularApiServer extends HttpServer { override val modules = Seq( UserRepositoryModule, SearchServiceModule, EngagementServiceModule) override def configurehttp(router: HttpRouter) { router.filter[loggingmdcfilter[request, Response]].filter[TraceIdMDCFilter[Request, Response]].add[PopularApiController]

Feature Testing class PopularApiFeatureTest extends FeatureTest with Mockito { val server = new EmbeddedHttpServer(new PopularApiServer) // Bind Mocks @Bind val databaseclient = mock[databaseclient] @Bind @SearchClient val searchclient = mock[httpclient] @Bind @EngagementsClient val engagementsclient = mock[httpclient] "Handle POST request" in { // Setup Mocks databaseclient.executesql(...) returns Future(SqlResponse(...) searchclient.execute(...) returns Future(SearchResponse(...)) engagementsclient.execute(...) returns Future(EngagementResponse(...)) server.httppost(...)

Feature Testing class val // Bind Mocks @Bind val databaseclient = mock[databaseclient] @Bind @SearchClient val searchclient = mock[httpclient] @Bind @EngagementsClient val engagementsclient = mock[httpclient] "Handle POST request" // Setup Mocks

Feature Testing class val // Bind Mocks @Bind @Bind @SearchClient @Bind @EngagementsClient "Handle POST request" // Setup Mocks databaseclient.executesql(...) returns Future(SqlResponse(...) searchclient.execute(...) returns Future(SearchResponse(...)) engagementsclient.execute(...) returns Future(EngagementResponse(...))

Consistency

Consistency Command Line App Long Running Server HTTP Server Thrift Server

Consistency: Thrift Server class PopularApiThriftServer extends ThriftServer { override val modules = Seq( UserRepositoryModule, SearchServiceModule, EngagementServiceModule) override def configurethrift(router: ThriftRouter) { router.filter[loggingmdcfilter[thriftrequest, ThriftResponse]].filter[TraceIdMDCFilter[ThriftRequest, ThriftResponse]].add[PopularApiThriftService]

Consistency: Http And Thrift Server class CombinedServer extends HttpServer with ThriftServer { override val modules = Seq(...) override def configurehttp(router: HttpRouter) { router.filter[loggingmdcfilter[request, Response]].filter[TraceIdMDCFilter[Request, Response]].add[PopularApiController] override def configurethrift(router: ThriftRouter) { router.filter[loggingmdcfilter[thriftrequest, ThriftResponse]].filter[TraceIdMDCFilter[ThriftRequest, ThriftResponse]].add[PopularApiThriftService]

Consistency: Thrift Feature Testing class PopularApiThriftFeatureTest extends FeatureTest { val server = new EmbeddedThriftServer(new PopularApiThriftServer) val client = server.thriftclient[popularapiservice.futureiface] "Handle populartweets" in { client.populartweets(...) should equal(...)

Consistency: Java Http Server public class PopularApiServer extends HttpServer { @Override public Collection<Module> javamodules() { return ImmutableList.<Module>of( new UserRepositoryModule(), new SearchServiceModule(), new EngagementServiceModule()); @Override public void configurehttp(httprouter httprouter) { httprouter.filter(commonfilters.class).add(popularapicontroller.class);

@Singleton public class JavaUserController extends JavaController { @Inject UserService userservice; @Override protected void configureroutes() { get( /users/:id, request -> userservice.lookup(request.getlongparam("id"))); Consistency: Http Controllers @Singleton class ScalaUserController @Inject()( userservice: UserService) extends Controller { get( /users/:id ) { request: Request => userservice.lookup(request.getlongparam("id"))

An End to End Example CREATING A POP ULAR TWEETS API

Popular API Requirements POST Request Path: /api/popular Header: api_key JSON Body: { "start": "2015-09-01", "end": "2015-09-30" HTTP 200 Response JSON Body: { "start": "2015-09-01T00:00:00Z", "end": "2015-09-30T00:00:00Z", "tweets": [ { "id": "123", "message": "hi", "counts": { "impressions": "20", "engagements": "10" ]

Feature Test class PopularApiFeatureTest extends FeatureTest { val server = new EmbeddedHttpServer(new HttpServer {) Handle POST request" in { server.httppost( path = "/api/popular", headers = Map("api_key" -> "secret123"), postbody = """{ "start": 2015-09-01",... """, andexpect = Status.Ok, withjsonbody = """ { "start" : "2015-09-01T00:00:00Z", "end" : "2015-09-01T00:00:00Z", "tweets" : [... ] "")

Test Failure ========================================================================= HTTP POST /api/popular [Header] Content-Length -> 204 [Header] Content-Type -> application/json;charset=utf-8 [Header] Host -> 127.0.0.1:60102 [Header] api_key -> secret123 { "start": "2015-09-01", "end": "2015-09-30" ========================================================================= RoutingService Request("POST /api/popular, from /127.0.0.1:60103) not found in registered routes: ------------------------------------------------------------------------- [Status] 404 Not Found [Header] Content-Length -> 0 *EmptyBody*

Request Parsing { "start": "2015-09-01", "end": "2015-09-30" case class PopularPostRequest( @PastTime start: DateTime, @PastTime end: DateTime, @Header @NotEmpty api_key: String)

Response Generation { "start": "2015-09-01T00:00:00Z", "end": "2015-09-30T00:00:00Z", "tweets": [ { "id": "123", "message": "hi", "counts": { "impressions": "20", "engagements": "10" ] case class ApiResponse( start: DateTime, end: DateTime, tweets: Seq[TweetResponse]) case class TweetResponse( id: TweetId, message: String, counts: Map[EngagementType, Long])

Controller @Singleton class PopularController @Inject()( userrepository: UserRepository, searchservice: SearchService, engagementservice: EngagementService) extends Controller { post("/api/popular") { request: PopularPostRequest => for { user <- userrepository.getbyapikey(request.apikey) tweets <- searchservice.findtweets(user, request) engagements <- engagementservice.lookup(user, tweets) yield { ApiResponse.create(request, tweets, engagements)

Server class PopularApiServer extends HttpServer { override def modules = Seq( UserRepositoryModule, SearchServiceModule, EngagementServiceModule) override def configurehttp(router: HttpRouter) { router.filter[commonfilters].add[popularcontroller]

Feature Test class PopularApiFeatureTest extends FeatureTest { val server = new EmbeddedHttpServer(new PopularApiServer) Handle POST request" in {...

But When We Run Our Tests ============================================================ HTTP POST /api/popular... ============================================================ GET /search/fullarchive/accounts/popularapi/prod.json Authorization -> Basic Og== internal server error com.twitter.server.namedresolvernotfoundexception: Resolver not found for scheme 'flag' with name 'data-api'.

Feature Test Mocking class PopularApiFeatureTest extends FeatureTest with Mockito { val server = new EmbeddedHttpServer(new PopularApiServer) // Bind Mocks @Bind val databaseclient = mock[databaseclient] @Bind @SearchClient val searchclient = mock[httpclient] @Bind @EngagementsClient val engagementsclient = mock[httpclient] "Post request" in { // Setup Mocks databaseclient.executesql(..) returns Future(SqlResponse(...) searchclient.execute(..) returns Future(SearchResponse(...)) engagementsclient.execute(..) returns Future(EngageResponse(..)) server.httppost(...)

Test Success! =========================================================================== HTTP POST /api/popular... { "start" : "2015-08-01", "end" : "2015-08-30" =========================================================================== 127.0.0.1 - - [15/Sep/2015:22:14:56 +0000] "POST /api/popular HTTP/1.1" 200 149 931 "-" --------------------------------------------------------------------------- [Status] Status(200)... { "start" : "2015-08-01T00:00:00Z", "end" : "2015-08-30T00:00:00Z", "tweets" : [ { "id" : "123", "message" : "Hi", "counts" : { "impressions" : "20", "engagements" : "10" ]

Let s Test A Bad Request "Post bad request" in { server.httppost( path = "/api/popular", postbody = """ { "start": "bad", "end": "2020-08-30" """, andexpect = Status.BadRequest)

Test Success ========================================================= HTTP POST /api/popular --------------------------------------------------------- [Status] Status(400) { "errors" : [ "api_key: header is required", "start: error parsing 'bad' into an ISO 8601 datetime", "end: [2020-08-30T00:00:00.000Z] is not in the past" ]

FINATRA Twitter s Production HTTP Framework Powerful Feature Testing Optional JSR-330 Dependency Injection Designed for Consistency and Modularity

Thank You @scosenza