Skip to content

Releases: finagle/finch

Finch 0.11-M2

17 Jul 18:07
Compare
Choose a tag to compare

This bug-fix release contains the following fixes since 0.11-M1:

  • There is now a ToResponse instance for Response (see #612)
  • Charset is not set for empty responses (see #613)
  • Coproducts that mix AsyncStream[?] now compile (see #616)

Finch 0.11-M1

13 Jul 18:17
Compare
Choose a tag to compare

This release is a result of fixing an old known issue when an encoder, resolved for a given type A doesn't take into an account a Content-Type of A. This is why, for example, an import io.finch.circe._ makes everything JSON.

This release also made it a new record: 24 contributors! Thank you!

Before looking closely into the changes were made, it's worth mentioning that there is now a Roadmap 1.0 available as a wiki page. It's not super clear yet how long it will take, but at least, we now have a finite list of things to-do.

Why M1?

This is just a first step (marked as M1) towards more type safety in Finch. While the solution released here is not complete, I decided to wrap up a release and let people play with initial bits, while postponing the rest of the things to M2. It's been almost 6 months since the previous release and I feel it's important to do a release (even the M-prefixed one) now, given that besides type-level content type, it also contains a number of improvements for memory and performance.

Please, note that the solution is presented here doesn't allow (doesn't compile) mixing endpoints of different content types in a single call .toService. While this sounds like a dramatic restriction, this should be viewed as a temporary step towards fully working solution that should a part of 0.11.

That said, please, consider upgrade if you only serve JSON (or anything) within your Finch server. Otherwise, would be reasonable to wait for 0.11.

Content-Type as a Type

There is now a much better way to handle content-types in Finch. A content-type is now a type-level string (see #541) and it does affect an implicit resolution of encoders, so, for example, if you ask for Encode.Text[A], and you only have Circe's encoders (JSON encoders) in the scope, your program won't compile. This a huge step towards a better utilization of Scala's type-system and capturing just a little more information in types.

Given that content-type is now a separate concept, which is neither attached to Endpoint nor Output, a way to specify it is to explicitly pass a requested content-type to a toService method call (using toServiceAs variant).

Please, note that Output.withContentType is gone since it was just a lie.

Before (broken at runtime):

// helloWorld is a text/plain endpoint that will be encoded by whatever encoder is in the scope
// (eg: if Circe is imported it will be a JSON string)
val api: Service[Request, Response] = (
  getUsers :+: postUsers :+: helloWorld
).toService

After (broken at compile time):

val api: Service[Request, Response] = (
  getUsers :+: postUsers :+: helloWorld
).toServiceAs[Application.Json]

JSON (i.e., Application.Json) is a default content-type for the to.Service call.

Charsets Make Difference

Previously, charsets set on Output don't make much of difference to the HTTP response service with Finch, rather than changing the Content-Type header. Now they are propagated to the encoder (see #610). For the sake of type-safety, a charset is now java.nio.charset.Charset, not String.

This change implies some default behaviour that should be explained. By default, a charset on Output is None so it's not copied over to the HTTP response. Although, an encoder will be using UTF-8 as a default. That said, everything will be encoded as UTF-8, but the Content-Type header won't be caring information about the charset.

Set charset either on Endpoint or Output to make it explicit (and propogated to the HTTP response).

New Endpoints

There are two new endpoints (see #606):

  • Endpoint.empty[A] that never matches
  • root: Endpoint[Request] that extracts an entire HTTP requests

Better Performance

Finch now uses Catbird's Rerunnable[A] instead of Eval[Future[A]] (see #578) and performs 20-30% better due to a reduced number of allocations.

Better Cats Integration

  1. There is now an Alternative instance for Endpoint provided out of the box (see #551).
  2. New endpoint paramsNel returns Cat's NonEmptyList (see #582)

Better Encoding

  1. It's now possible to JSON stream anything that has a JSON encoder (see #566)
  2. Anything that has a cats.Show instance now derives Encode.PlainText and so might be served as text/plain (see #561)

New Packages

  1. Support for Play JSON (see #538)
  2. Support for Spray JSON (see #568)

Better Generic Derivation

Endpoint.derive[Foo].fromParams now takes into an account optional params deriving paramOption and multi-value params deriving either params or paramsNel (see #577).

Updated Dependencies

  1. Finagle 6.35
  2. Circe 0.5.0-M2
  3. Cats 0.6
  4. Shapeless 2.3

Finch 0.10

16 Feb 19:52
Compare
Choose a tag to compare

This release is about unifying request readers and endpoints (see #524): now there is only Endpoint[A] abstraction in Finch that takes a request and returns a value of type A. RequestReader are just a type alias for Endpoint for now.

Updated dependencies

  • Finagle 6.33
  • Circe 0.3
  • Cats 0.4.1

Breaking API Changes

  • RequestReader.flatMap is gone - use :: instead

New Features

  • An Output ADT now has a third case - an Empty output (see #508)
  • Request readers are now endpoints (see #524)
  • There is now very basic support for Buf-streaming (see #515 and example)

As usual, all the docs, best-practices and cookbook recipes have been updated.

Finch 0.9.3

22 Dec 21:02
Compare
Choose a tag to compare

This release fixes a couple of silly bugs that have been introduced during performance optimizations on body readers as well as put some new examples and best practices.

Finch 0.9.2

05 Dec 17:25
Compare
Choose a tag to compare

This release contains a variety of performance improvements for body readers and extracting endpoints as well as new API for failures.

Upgraded Dependencies

  • Finagle 6.31
  • Circe 0.2.1
  • Cats 0.3.0 (yes! Finch now uses Cats' Eval to empower endpoints' laziness)

Breaking API Changes

  • All the finch-core is now available by a single import io.finch._. To migrate remove imports io.finch.request.* from your codebase.
  • io.finch.request.RequestError has been renamed to io.finch.Error.
  • x.toFuture and x.toFutureException have been removed. Use Future.value and Future.exception instread.

Deprecations

  • Output.Failure(map: Map[String, String]) is deprecated in favor of Output.Failure(cause: Exception) and will be removed in 0.9.3.

Performance Improvements

This is the first Finch release with first bits of performance work:

  • 30% less allocations and 20% less running time for body* readers.
  • 10% less allocations and 10% less running time for extractors int, string, long, boolean.

The bottom line is Finch now gives only 5-6% overhead (both allocations and running time) on top of bare metal Finagle.

New Features

New Failures API

Output.Failure now wraps Exception, which might be encoded into an HTTP response by EncodeResponse[Exception]. See docs for more details.

Pretty Printers for Argonaut and Circe

It's now possible to override the default pretty printer for Circe and Argonaut. For example, the following import defines Circe's encoder that drops null keys in JSON objects. See docs for more details.

import io.finch.circe.dropNullKeys._

New Examples and Best Practices

  • There is a new sub-project examples that contains always up-to-date Finch examples.
  • New docs section called "Best Practices" is aimed to collect all the known approaches and techniques that work reasonably good with Finch. Feel free to open a PR to share your experience.

Finch 0.9.1

12 Nov 04:22
Compare
Choose a tag to compare

This release contains a bug fix discovered by @nafg and fixed by @imliar.

Breaking API Changes

This release is source compatible with most of the Finch API 0.9.0 except for Outputs API.

Before:

val ok: Output[Unit] = Ok
val err: Output[Nothing] = BadRequest

After:

val ok: Output[Unit] = Ok()
val err: Output[Nothing] = BadRequest()

New Features

Finch now provides a very basic instance of EncodeResponse[Map[String, String]], which is unlikely useful for production projects but it's helpful to provide this out of the box and be more friendly for newcomers.

Finch 0.9.0

09 Nov 01:55
Compare
Choose a tag to compare

This release is mostly about reworking the Endpoint API. Thanks to all the amazing contributors who helped to make this happen: @travisbrown, @imliar, @arnihermann, @rpless, @roanta, @longcao and many more!

As usual, all the docs have been upgraded to the current state!

Upgraded Dependencies

  • Finagle 6.30
  • Circe 0.2.0

Breaking API Changes

  • Finagle HTTPx renamed to Finagle HTTP. Use this guide to migrate.

Deprecations

  • io.finch.route.Router (deprecated) is renamed to io.finch.Endpoint
  • /> and />> combinators are deprecated - use Endpoint.apply instead
  • io.finch.response package is deprecated - use io.Finch.Output instead
  • HTTP verb matchers Get, Post, etc are deprecated - use functions get: Endpoint[A] => Endpoint[A] instead

New Features

Finch ♥ OAuth2

finagle-oauth2 is now supported in Finch! See docs for more details.

UUIDs

java.util.UUIDs are supported out of the box in all the possible settings:

  • as request reader param("uuid").as[UUID]: RequestReader[UUID]
  • as endpoint uuid: Endpoint[UUID]

Output

The new way of capturing the output context: headers, cookies, content type, etc. See outputs for more details.

Error Handling

Brand new methods Endpoint.handle and Endpoint.rescue allows to handle the failed future from the endpoint. See error handling for more details.

Finch 0.8.0

10 Aug 15:53
Compare
Choose a tag to compare

This release introduces a bunch of neat features as well as some housekeeping cleanups.

Breaking API Changes

  • finch-auth has been removed from the project (see new feature "Basic HTTP Auth")
  • Type aliases io.finch.HttpRequest and io.finch.HttpResponse were removed (use Finagle's types instead)
  • The flatMap method on Router has been replaced with ap

Deprecated API

  • All the old-style endpoints are deprecated: io.finch.Endpoint and io.finch.Endpoint in favour of coproduct routers
  • The custom request types are also deprecated (see this note)
  • All the implicit classes (the bang! operator) on top of Service and Filter are deprecated

New Packages

New Features

Basic HTTP Auth

Use the following API in order to enable Basic HTTP Auth on the given Router.

val r: Router[String] = Router.value("protected")
val authR: Router[String] = basicAuth("username", "password")(r)

Smart Routers Composition

When compositing two coproduct Routers the yielded Router will always be flattened.

val r1: RequestReader[Int :+: String :+: CNil] = Router.value(10) :+: Router.value("s1")
val r2: Router[Boolean :+: String :+: CNil] = Router.value(true) :+: Router.value("s2")
val r3: Router[Int :+: String :+: Boolean :+: String :+: CNil] = r1 :+: r2

Compose RequestReaders and Routers

Since 0.8.0 it's possible to compose Router and RequestReader together with the ? combinator.

val r1: RequestReader[Int :: String :: HNil] = param("a").as[Int] :: param("b")
val r2: Router[Boolean] = Router.value(true)
val r3: Router[Boolean :: Int :: String :: HNil] = r2 ? r1

Method Matchers

It's now recommended to use new API for matching the HTTP methods.

// Before
val r1: Router0 = Get / "users"

// After
val r2: Router0 = get("users")

Router Mapper

There is a new API for mapping routers to either functions A => B or A => Future[B]. Use an apply method on Router instead of methods />> and >, which are going to be deprecated in 0.9.0.

val r1: Router[Int] = get("users" / int) { id: Int => id * 42 }
val r2: Router[String] = get("hello") { Future.value("Hello, World!") }

There is just one downside of this feature: you have to always specify all the types in the function you're passing.

Tail Extractors

For each route extractor (i.e., int, string), there is now a corresponding tail extractor that extracts a Seq[A] from the tail of the current route.

// will match "/users/10/20/30" and extract Seq(10, 20, 30)
val r: Router[Seq[Int]] = "users" / ints

Finch 0.7.0

08 Jun 16:44
Compare
Choose a tag to compare

This release is mostly a result of @travisbrown's heroic efforts on replacing ugly hacks (eg. types / and ~) with Shapeless' primitives. As a result, a bunch of API has been marked deprecated.

Some inspiring statistics:

  • 17 contributors pushed 136 commits since 0.6.0, which is a new record
  • 3 new adopters since 0.6.0, which is a new record

Breaking API Changes

  • the / type (case class) is removed in favour of HList (see details below)

Deprecated API

  • finch-json is completely deprecated (and will be removed in 0.8.0) in favour finch-argonaut
  • finch-jawn is deprecated (and will be removed in 0.8.0) in favour other JSON libraries
  • both ~ type (case class) and ~ RequestReaders compositor are deprecated in favour of HList-based readers (see details below)
  • finch-micro is deprecated (and will be removed in 0.8.0) in favour of coproduct routers (see details below)

New Packages

  • finch-json4s - JSON4S support in Finch (contributed by @imliar)

New Infrastructure

Finch is moving towards better stability and performance by integrating well-known tools: ScalaCheck and JMH. Property-based testing helped us to completely cover the JSON packages (and some API fromfinch-core) with tests. Benchmarking allowed us to establish the initial performance characteristic of the major Finch components.

New Features

Most of the new features have landed in the finch-core package.

HList-powered RequestReader

The :: compositor is now used instead of ~ in the applicative syntax of RequestReader. The main different is that :: constructs a RequestReader[A :: B :: HNil] instead of RequestReader[A ~ B]. The main advantage of this migration is that a well-known method as is now can be used more generically, allowing to convert any underlying HList into a case class.

case class Foo(i: Int, s: String)
val foo: RequestReader[Foo] =
  (param("i").as[Int] :: param("s")).as[Foo]

See section "Applicative Syntax" for more details.

HList-powered Router

The / case class has been replaced with HList. This change shouldn't affect end users, except for the cases when / extractor is used. See section "Composing Routers" for more details.

Coproduct Routers

This is another attempt (as well as finch-micro) to solve the problem of programming with types that matter. The basic idea behind this is that it's now possible to compose two routers of different types using the :+: compositor that constructs a Router[C <: Coproduct].

val router: Router[Foo :+: Bar :+: CNil] =
  (foo: Router[Foo]) :+: (bar: Router[Bar])

The killer feature of coproduct routers is that they can be safely converted into a Finagle services using the toService method.

val router: Router[String :+: HttpResponse :+: CNil] = ???
val service: Service[HttpRequest, HttpResonse] = router.toService

See section "Coproduct Routers" for more details.

Finch 0.6.0

27 Mar 23:19
Compare
Choose a tag to compare

This release involves several improvements of the core package as well as a new "easter egg" mode of Finch with codename "Your REST API as a Monad". Although, this release is 100% source-compatible with a previous version.

Please, note that while the old-style io.finch.Endopint is not deprecated yet, it's highly recommended to consider migration to route combinators.

Deprecations

There is a new naming conversion for RequestReaders that is heavily inspired by Scala standard library:

  • Every RequiredX is now just x, eg. param("a")
  • Every OptionalX is now xOption, eg. headerOption("X-Secret")

There is just one exclusion from this scheme:

  • RequiredParams is now paramsNonEmpty
  • OptionalParams is now params

New Features

  • There is a secret Finch mode available: deal with types that matter (see details bellow)
  • The RequestReader is now polymorphic in terms of request type (see details bellow)
  • There are two new RequestReaders: fileUpload and fileUploadOption that can read an upload (a multipart/form) param from the request
  • Both paramand paramOption are also looking at multipart/form content for param values
  • Two new combinators ~> and ~~> are available on RequestReader (see details bellow)
  • The /> combinator on Router is now more useful (see details bellow)
  • There is router ** that always matches an entire route
Your REST API as a Monad

There is a secret (ninja) mode available in this release. It's fully described in docs/micro.md. The basic idea is to replace Service (a function) with RequestReader (a monad), which basically does the same job but implies insane composability. While it's still an experimental approach, we believe that it solves the "Finch Puzzle" and dramatically simplifies the whole picture. We personally believe that his way is "the way" to write idiomatic and robust Finch code.

Polymorphic RequestReader[A] a la PRequestReader[R, A]

While this tiny change doesn't affect API, this is a huge step towards flexibility. Code speaks for them self, here is the current picture:

trait PRequestReader[R, A] {
  def apply(req: R): Future[A]
}

type RequestReader[A] = PRequestReader[HttpRequest, A]

That said, all the monadic and applicative operations are defined over the output type A (right associative).

Magic behind ~>, ~~> and /> combinators

Both ~> and ~~> compositors allow to threat underlying type A ~ B .. ~ Z as (A, B, ..., Z). So, the following code compiles:

def sum(i: Int, j: Int) = i + j
val r: RequestReader[Int] = 
  param("i").as[Int] ~ param("j").as[Int] ~> sum

Using the ~~> we can make long calls to the underlying async API (considering that Micro is RequestReader):

def fetchUserFromDb(id: Int): Future[User] = ???
def getUser: Micro[User] = RequiredParam("id").as[Int] ~~> fetchUserFromDb

Compositor /> basically implies the same idea but for types A / B / C ... Z.


By tradition, kudos to awesome contributors: @jenshaase, @imliar and @suls!