Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save getmetorajesh/fbdfa71467d5c011bd588ed8d9d8ae98 to your computer and use it in GitHub Desktop.
Save getmetorajesh/fbdfa71467d5c011bd588ed8d9d8ae98 to your computer and use it in GitHub Desktop.
simple Akka Http API including links according to JSON API spec http://jsonapi.org/
import akka.actor.ActorSystem
import akka.event.LoggingAdapter
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.marshalling.{ToEntityMarshaller, Marshaller}
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import akka.stream.Materializer
import com.oxyme.monitoring.HealthChecker
import com.oxyme.monitoring.model.{Unhealthy, Healthy}
import com.typesafe.config.{ConfigFactory, Config}
import akka.http.scaladsl.model.StatusCodes._
import spray.json._
import spray.json.DefaultJsonProtocol
import scala.concurrent.ExecutionContextExecutor
case class Person(name: String, age: Int)
case class Links(self: String, first: String, next: Option[String], prev: Option[String], last: String)
case class Persons(total: Int, links: Links, data: Iterable[Person])
object PersonsLinks {
def apply(total: Int, maybePage: Option[Int], maybePageSize: Option[Int], params: (String, String)*): Links =
LinkBuilder.links("persons", total, maybePage, maybePageSize, params)
}
object LinkBuilder {
val config = ConfigFactory.load()
val host = config.getString("http.host")
val port = config.getInt("http.port")
def links(resource: String, total: Int, maybePage: Option[Int], maybePageSize: Option[Int], params: Iterable[(String, String)]) = {
val page = maybePage.getOrElse(1)
val (first, last) = (1, maybePageSize match {
case Some(pageSize) if pageSize > 0 => Math.ceil(total.toDouble / pageSize).toInt
case _ => 1 // show all results on page 1 by default
})
Links(
link(resource, params, page, maybePageSize),
link(resource, params, first, maybePageSize),
if(last > page) Some(link(resource, params, Math.min(page + 1, last), maybePageSize)) else None,
if(page > first) Some(link(resource, params, Math.min(page - 1, last), maybePageSize)) else None,
link(resource, params, last, maybePageSize)
)
}
def link(resource: String, params: Iterable[(String, String)], page: Int, pageSize: Option[Int]): String =
Uri(s"http://$host:$port/$resource").withQuery(params.toSeq ++ paginationParams(page, pageSize):_*).toString
private def paginationParams(page: Int, pageSize: Option[Int]): Seq[(String, String)] = {
Seq("page" -> Some(page), "pageSize" -> pageSize).collect {
case (paramName, Some(value)) => paramName -> value.toString
}
}
}
trait Protocols extends DefaultJsonProtocol {
implicit val personFormat = jsonFormat2(Person.apply)
implicit val linksFormat = jsonFormat5(Links.apply)
implicit val personsFormat = jsonFormat3(Persons.apply)
val `application/vnd.persons.v1+json` =
MediaType.custom("application/vnd.persons.v1+json", MediaType.Encoding.Fixed(HttpCharsets.`UTF-8`))
implicit val personsMarshaller: ToEntityMarshaller[Persons] = Marshaller.opaque { persons =>
HttpEntity(ContentType(`application/vnd.persons.v1+json`, HttpCharsets.`UTF-8`), persons.toJson.compactPrint)
}
}
trait Service extends Protocols {
implicit val system: ActorSystem
implicit val materializer: Materializer
// declared here because it needs Materializer as opposed to Marshaller
implicit val personsUnmarshaller = sprayJsonUnmarshaller[Persons].forContentTypes(
`application/vnd.persons.v1+json`,
`application/json`
)
private def offset(page: Option[Int], pageSize: Option[Int]) =
(page.getOrElse(1) - 1) * pageSize.getOrElse(0) // show all results by default
val routes = {
path("persons"){
get {
parameters('page.as[Int]?, 'pageSize.as[Int]?) { (page, pageSize) =>
complete {
val offset = offset(page, pageSize)
val persons = Seq(Person("jeroen", 28), Person("harry", 12), Person("john", 31))
val pagedPersons = data.drop(offset).take(pageSize.getOrElse(data.size))
val links = PersonsLinks(persons.size, page, pageSize)
Persons(persons.size, links, pagedPersons)
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment