Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Scala at Netflix

Scala at Netflix

My talk at Scala Bay Meetup at Netflix about Powering the Partner APIs with Scalatra and Netflix OSS. This talk was delivered on September 9th 2013, at Netflix, Los Gatos.

Manish Pandit

September 09, 2013
Tweet

More Decks by Manish Pandit

Other Decks in Programming

Transcript

  1. Scala at Netflix Scala Bay Meetup Netflix, Sept 9th 2013

    Manish Pandit mpandit@netflix.com @lobster1234
  2. Architecture Cassandra HTTP  Layer,  and  Manager  Layer   EVCache  

    Crowd/SSO RDS Astyanax Netflix OSS Cloud Components
  3. /** Lets try to build a login endpoint. It should

    support a method called login(user:String,pass:String) that returns an Option[String]. */ class LoginManagerSpec extends FlatSpec with ShouldMatchers { }
  4. /** Lets try to build a login endpoint. It should

    support a method called login(user:String,pass:String) that returns an Option[String]. */ class LoginManagerSpec extends FlatSpec with ShouldMatchers { it should " Be able to login a valid user and get a token " in { fail() } }
  5. /** Lets try to build a login endpoint. It should

    support a method called login(user:String,pass:String) that returns an Option[String]. */ class LoginManagerSpec extends FlatSpec with ShouldMatchers { it should " Be able to login a valid user and get a token " in { val token = LoginManager.login("someuser", "somepassword") token should not be None } }
  6. /** Lets try to build a login endpoint. It should

    support a method called login(user:String,pass:String) that returns an Option[String]. */ class LoginManagerSpec extends FlatSpec with ShouldMatchers { it should " Be able to login a valid user and get a token " in { val token = LoginManager.login("someuser", "somepassword") token should not be None } it should " Fail to login an invalid user " in { fail } }
  7. /** Lets try to build a login endpoint. It should

    support a method called login(user:String,pass:String) that returns an Option[String]. */ class LoginManagerSpec extends FlatSpec with ShouldMatchers { it should " Be able to login a valid user and get a token " in { val token = LoginManager.login("someuser", "somepassword") token should not be None } it should " Fail to login an invalid user " in { val token = LoginManager.login("fail", "fail") token should be (None) } }
  8. A Simple API in Scalatra class LoginService extends ScalatraServlet {

    before() { contentType = formats("json") } post("/") { val token = LoginManager.login(params("user"), params("password")) token match{ case None => Unauthorized(Map("message"->"Login failed")) case Some(x) => Ok(Map("token"->x)) } } }
  9. Putting it all together.. class ScalatraBootstrap extends LifeCycle { override

    def init(context: ServletContext) { context.mount(new LoginService, "/login/*") } } <listener> <listener-class>org.scalatra.servlet.ScalatraListener</listener-class> </listener>
  10. ScalatraSpec traits to take ScalaTest to the next level support

    for all HTTP methods helpers for JSON parsing plenty of wrappers (body, headers..)
  11. class LoginServiceSpec extends ScalatraFlatSpec { addServlet(classOf[LoginService], "/*") it should "log

    in valid users" in { post("/", body = """user=gooduser&password=goodpassword""") { status should equal(200) body should include "token" } } }
  12. class LoginServiceSpec extends ScalatraFlatSpec { addServlet(classOf[LoginService], "/*") it should "log

    in valid users" in { post("/", body = """user=gooduser&password=goodpassword""") { status should equal(200) body should include "token" } } it should "not allow invalid users to log in" in { post("/", body = """user=baduser&password=badpassword""") { status should equal(401) body should include "message" } } }
  13. APIs Best Practices Use Proper HTTP Response Codes Set Proper

    HTTP Headers Break up your data into groups
  14. Git Workflow $ git status # On branch dev #

    Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: src/main/scala/com/netflix/nrdportal/http/DpiService.scala # modified: src/test/scala/com/netflix/nrdportal/http/DpiServiceSpec.scala
  15. Automated Code Pushes Push to dev Jenkins runs dev build,

    tests, merges to master Jenkins runs master build, makes an RPM Aminator bakes an AMI from the RPM asgard deploys the AMI in staging cloud
  16. Using Options as Wrappers …where you have to call code

    that can return null def returnNull(x:Int) = if(x%2 == 0) "Even" else null scala> Option(returnNull(3)) res01: Option[String] = None scala> Option(returnNull(2)) res02: Option[String] = Some(Even)
  17. Using Try[A] def someFunction(x: Int): Try[String] = { if (x

    % 2 == 0) Success("Even") else Failure(new IllegalArgumentException("Odd number!")) }
  18. Control Abstractions def withAuthenticatedUser(f: (String) => ActionResult) = { getUserNameFromCookie

    match { case Some(userName) => f(userName) case None => Unauthorized("You are not logged in!") } } def printCurrentUserName = { withAuthenticatedUser { userName => Ok(s"Your username is ${userName}") } }
  19. Finally… Avoid writing complex code at all costs – there

    are better ways to prove your awesomeness!