github finagle/finch 0.4.0
Finch 0.4.0

latest releases: v0.34.1, v0.34.0, v0.33.0...
9 years ago

Highlights

  • New package jackson - a Jackson support
  • New method ResponseBuilder.withCookies()
  • Updated demo with custom request type and implicit conversion: Seq[ToJson] => ToJson
  • Scaladoc

Finch becomes more general! Three major components of Finch were generalised in this release:

  • Endpoint is no longer depend on HttpRequest: any Req type may be used instead
  • ResponseBuilder is no longer depend on JSON response type: any media type may be built with it
  • RequestReader is no longer depend on JSON: any type A may be read with RequiredBody[A] and OptionalBody[A]
Implicit view Req => HttpRequest instead sub-typing

Finch moved forward to composition from inheritance. Since, 0.4.0 release, an Endpoint changed its type-parameters bound from Endpoint[Req <: HttpRequest] to Endpoint[Req, Rep]. Thus, in fact, any request type may be used in the endpoint, even the custom case class like

// we compose MyRequest with HttpRequest but not extend it
case class MyRequest(http: HttpRequest)
val e: Endpoint[MyRequest, HttpResponse]

There is also an implicit conversion in io.finch._ that converts Endpoint[Req, Rep] to Finagle Service[Req, Rep]. Thus, the method Endpoint.toService is no longer exist. In order to enable the implicit conversion there is should be an implicit view Req => HttpRequest available in the scope. For example for MyRequest it looks like:

implicit val myReqEv = (req: MyRequest) => req.http

Having an implicit view imported in the scope, an Endpoint may be treated as a usual Finagle Service:

case class MyRequest(http: HttpRequest)
implicit val myReqEv = (req: MyRequest) => req.http
val e: Endpoint[MyRequest, HttpResponse]
// an endpoint `e` will be converted to service implicitly
Httpx.serve(new InetSocketAddress(8081), e)

Note, that in case of using pure HttpRequest and HttpEndpoint there is no need to define an implicit view from HttpRequest => HttpRequest since it's already defined in Scala's Predef.

This new functionality is also supported by RequestReader, which previously had a signature:

trait RequestReader[A] {
  def apply(req: HttpRequest): Future[A]
}

Since release 0.4.0 RequestReader takes any request type, which has an implicit view to HttpRequest. In fact, signature has been changed to:

trait RequestReader[A] {
  def apply[Req](req: Req)(implicit ev: Req => HttpRequest): Future[A]
}

This allows to use requests readers smoothly even with custom request types like MyRequest:

case class MyRequest(http: HttpRequest)
implicit val myReqEv = (req: MyRequest) => req.http
val req: MyRequest = ???
// we don't need to call it as `req.http` since there is an implicit view available
val s: Future[String] = RequiredParam("name")(req)

Finch provides the developers all the suitable abstractions to switch from inheritance to composition. While it's still possible to extends HttpRequest and pass it around, the composition in form MyRequest(http: HttpRequest) is preferred.

Generalized EncodeResponse/DecodeRequest instead of EncodeJson/DecodeJson

Finch moved away from JSON dependency to general concept of some type A that might be decoded from request using the DecodeRequest and encoded to response using the EncodeResoponse. This gives the opportunity to support not just JSON but any format (i.e., XML, EDN or even custom case class). Unless it looks like a big change, the JSON support via pluggable libraries remanned the same with type changes in API:

  • RequiredBody/OptionalBody renamed to RequiredArrayBody/OptionalArrayBody
  • RequiredJsonBody/OptionalJsonBody renamed to RequiredBody/OptionalBody

Thus the usage of new API with finch-json looks like:

val readJson: RequestReader[Json] = RequiredBody[Json]

The following example demonstrates the power of new concept by defining a custom decoder for Double values. Thus the double values encoded in request body may be read with predefined reader RequiredBody.

implicit val decodeDouble = new DecodeRequest[Double] {
  def apply(s: String): Option[Double] =
    try { Some(s.toDouble) } catch { case _: NumberFormatException => None }
}
val req: HttpRequest = ???
val readDouble: RequestReader[Double] = RequiredBody[Double]
val double = readDouble(req)

Grab on Maven Central:

libraryDependencies ++= Seq(
  "com.github.finagle" %% "[finch-module]" % "0.4.0"
)

As always kudos to awesome contributors: @pasviegas, @rodrigopr, @rpless, @travisbrown!

Don't miss a new finch release

NewReleases is sending notifications on new releases.