Akka-HTTP

Akka-HTTP provides a nice DSL to describe a server-side API similar to doobie. But Akka's language flows a bit differently. Instead of pattern-matching the request by applying rules one by one, it works more like a sieve. It filters requests by providing a number of directives, each matching some aspect of the request. The directives are nested so that each request travels deeper and deeper into matching branches until it reaches the processing logic. Such a combination of directives is called route. This is the inventory route:

lazy val inventoryRoutes: Route =
path("inventory") {
get {
???
}
} ~
path("purchase") {
post {
entity(as[PurchaseArticles]) { order =>
???
}
}
} ~
path("restock") {
post {
entity(as[RestockArticles]) { stock =>
???
}
}
}

This route contains three smaller routes, one matching GET /inventory, another POST /purchase, and the third POST /restock. The second and third routes also define that the request entity must be parseable as PurchaseArticles and RestockArticles respectively and provide the result of the parsing as a parameter to the body. Let's see how the internals of these routes are implemented. We know that the inventory route should return the current state so we ask the inventory actor about that:

complete((inventory ? GetInventory).mapTo[Inventory])

The complete method takes ToResponseMarshallable, and we rely on the Akka-HTTP and JSON serializers we defined earlier to do the implicit conversion from the Future[Inventory] that we're getting as the result of the application of the ask pattern here.

inventory is provided as an abstract field for now. This is how it looks in the definition of Routes:

trait Routes extends JsonSupport {
implicit def system: ActorSystem
def inventory: ActorRef
def config: Config

implicit lazy val timeout: Timeout = config.timeout
implicit lazy val ec: ExecutionContext = system.dispatcher

We define an abstract config that we then use to define an implicit timeout for the ask. We also define ExecutionContext in order to be able to map over Future in other routes.

The implementation of the other two routes is similar. This is the purchase route:

val response: Future[Option[ArticlesPurchased]] =
(inventory ? order).mapTo[Option[ArticlesPurchased]]
onSuccess(response) {
case None => complete(StatusCodes.Conflict)
case Some(event) => complete(event)

The logic is almost the same with differences related to the situation we can't satisfy the requirements. In this case, we return the 409 Conflict error code.

The restock route is even simpler because it always succeeds:

val response: Future[Option[ArticlesRestocked]] =
(inventory ? stock).mapTo[Option[ArticlesRestocked]]
complete(response)

The definition of articleRoute for article creation and deletion is very similar and is available on GitHub so we will omit it here. 

The routes are combined together using ~, the same way we already did inline:

lazy val routes: Route = articlesRoutes ~ inventoryRoutes
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.21.152