Skip to content

Instantly share code, notes, and snippets.

@fntz
Created January 6, 2015 16:03
Show Gist options
  • Save fntz/4554bafb9b3cb4ed034c to your computer and use it in GitHub Desktop.
Save fntz/4554bafb9b3cb4ed034c to your computer and use it in GitHub Desktop.
Rest Api with spray
import akka.io.IO
import akka.actor.{ActorSystem, Props}
import spray.can.Http
object Boot extends App {
implicit val system = ActorSystem("rest")
val restService = system.actorOf(Props[RestApiService], "rest-api")
IO(Http) ! Http.Bind(restService, interface = "localhost", port = 8000)
}
import com.github.fntzr.spray.routing.ext.BaseController
import spray.http.HttpHeader
import spray.http.MediaTypes._
import spray.httpx.marshalling.ToResponseMarshallable
import spray.routing._
import spray.http.StatusCodes._
import scala.collection.mutable.ArrayBuffer
import spray.routing.HttpService
import java.util.UUID
import shapeless._
import spray.json.DefaultJsonProtocol
import spray.httpx.SprayJsonSupport._
case class Post(text: String)
case class User(name: String)
object Db {
val user = User("admin")
val postList = ArrayBuffer[Post](Post("some content#1"), Post("some content#2"), Post("some content#3"))
val authTokenList = ArrayBuffer[(String, User)]()
val userList = List(user)
}
trait AuthValues {
val authToken = "authToken"
val authTokenHeader = "X-AUTH-TOKEN"
}
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val userFormats = jsonFormat1(User)
implicit val postFormats = jsonFormat1(Post)
}
trait SecurityHelper extends AuthValues {
import HttpService._
def protect: Directive[User::HNil] = {
optionalHeaderValueByName(authTokenHeader).flatMap {
case Some(token) =>
Db.authTokenList.find(t => t._1 == token) match {
case Some(t) => provide(t._2)
case None => complete(NotAcceptable, s"Token $token not found")
}
case None => complete(Unauthorized, s"$authTokenHeader is empty")
}
}
}
trait RespondHelper {
import HttpService._
def r(result: ToResponseMarshallable) = respondWithMediaType(`application/json`) {
complete(result)
}
}
trait AuthController extends BaseController with SecurityHelper with RespondHelper {
import HttpService._
import MyJsonProtocol._
def login = {
entity(as[User]) { user =>
Db.userList.find(u => u == user) match {
case Some(user) =>
val token = UUID.randomUUID().toString
Db.authTokenList append ((token, user))
r(s"""{ "$authToken": "${token}" }""")
case None =>
r(Unauthorized, "Check name")
}
}
}
}
trait PostController extends BaseController with SecurityHelper with RespondHelper {
import HttpService._
import MyJsonProtocol._
def index = r(Db.postList.toList)
def show(id: Int) = r {
Db.postList.lift(id) match {
case Some(post) => post
case None => (NotFound, s"Post with $id not found.")
}
}
def create = protect { user =>
entity(as[Post]) { post =>
Db.postList append post
r(""" { "result": "ok" } """)
}
}
def delete(id: Int) = protect { user =>
Db.postList.lift(id) match {
case Some(post) =>
r(""" { "result": "ok" } """)
case None => r(NotFound, s"Post with ${id} not found")
}
}
}
import akka.actor.Actor
import com.github.fntzr.spray.routing.ext.{Routable, exclude}
trait RestRoute extends Routable {
val route =
scope("api") {
scope("v1") {
resource[PostController, Post](exclude("new", "edit", "update", "create"), {
post0[PostController]("create")
}, IntNumber) ~
post0[AuthController]("login")
}
}
}
class RestApiService extends Actor with RestRoute {
def actorRefFactory = context
def receive = runRoute(route)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment