Skip to content

Commit

Permalink
Fix 179: Restructure docs
Browse files Browse the repository at this point in the history
  • Loading branch information
vkostyukov committed Feb 10, 2015
1 parent ed7f816 commit 6c43fb6
Show file tree
Hide file tree
Showing 10 changed files with 284 additions and 251 deletions.
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ elsewhere.

* Code and comments should be formatted to a width no greater than 120 columns
* Files should be exempt of trailing spaces
* Each abstraction should live in their own Scala file, i.e `RequestReader.scala`
* Each implementation should live in the package object, i.e, `io.finch.request`

That said, the Scala style checker `sbt scalastyle` should pass on the code.

Expand Down
18 changes: 8 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ Finch is a thin layer of purely functional basic blocks atop of [Finagle](http:/
building composable REST APIs. Its mission is to provide the developers simple and robust REST API primitives being as
close as possible to the bare metal Finagle API.

Status
Badges
------
[![Build Status](https://travis-ci.org/finagle/finch.svg?branch=master)](https://travis-ci.org/finagle/finch)
[![Coverage Status](https://coveralls.io/repos/finagle/finch/badge.svg?branch=master)](https://coveralls.io/r/finagle/finch?branch=master)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/finagle/finch?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

Modules
-------
Expand Down Expand Up @@ -62,14 +63,9 @@ Httpx.serve(":8080",

Documentation
-------------
* A comprehensive documentation may be found in the [`docs.md`](docs.md) file in the root directory
* The latest Scaladoc is [here](http://finagle.github.io/finch/docs/#io.finch.package)

Contacts
--------

* Most of the discussions and announcements are happen at [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/finagle/finch?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

* A comprehensive documentation may be found in the [`docs/`](docs/index.md) folder
* The latest Scaladoc is available at [http://finagle.github.io/finch/docs](http://finagle.github.io/finch/docs/#io.finch.package)

Adopters
--------
* [Konfettin](http://konfettin.ru)
Expand All @@ -83,7 +79,7 @@ Contributing
There are plenty of ways to contribute into Finch:

* Give it a star
* Join the [Gitter](https://gitter.im/finagle/finch) room and leave a feedback or help with answering users' questions
* Join the [Gitter][1] room and leave a feedback or help with answering users' questions
* [Submit a PR](CONTRIBUTING.md)
* Be cool and wear a [Finch T-Shirt](http://www.redbubble.com/people/vkostyukov/works/13277123-finch-io-rest-api-with-finagle?p=t-shirt)

Expand All @@ -98,3 +94,5 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

[1]: https://gitter.im/finagle/finch
8 changes: 4 additions & 4 deletions core/src/main/scala/io/finch/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import com.twitter.finagle.{Filter, Service}
* Get / ("users" | "user") / int /> GetUser
* }}}
*
* Step 2: Reading the HTTP requests in a `Service`.
* Step 2. Reading the HTTP requests in a `Service`.
*
* The [[io.finch.request.RequestReader RequestReader]] abstraction is responsible for reading any details form the HTTP
* request. `RequestReader` is composable in both ways: via the monadic API (using the for-comprehension, i.e.,
Expand All @@ -56,16 +56,16 @@ import com.twitter.finagle.{Filter, Service}
*
* {{{
* val pagination: RequestReader[(Int, Int)] =
* OptionalIntParam("offset") ~ OptionalIntParam("limit") map {
* OptionalParam("offset").as[Int] ~ OptionalParam("limit").as[Int] map {
* case offset ~ limit => (offset.getOrElse(0), limit.getOrElse(100))
* }
* val p = pagination(request)
* }}}
*
* Step 3. Building the HTTP responses in a [[Service]].
* Step 3. Building the HTTP responses in a `Service`.
*
* The [[io.finch.response.ResponseBuilder ResponseBuilder]] abstraction provides a convenient way of building the HTTP
* responses any type. In fact, `ResponseBuilder` is a function that takes some content and builds an HTTP response of a
* responses any type. In fact, ResponseBuilder` is a function that takes some content and builds an HTTP response of a
* type depending on a content. There are plenty of predefined builders that might be used directly.
*
* {{{
Expand Down
19 changes: 19 additions & 0 deletions docs/auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## Authentication

### Authorization with OAuth2

There is [finagle-oauth2](https://github.com/finagle/finagle-oauth2) server-side provider that is 100% compatible with
Finch.

### Basic HTTP Auth

[Basic HTTP Auth](http://en.wikipedia.org/wiki/Basic_access_authentication) is supported out-of-the-box and implemented
as `finch.io.auth.BasicallyAuthorize` filter.

```scala
object ProtectedEndpoint extends Endpoint[HttpRequest, HttpResponse] {
def route = {
case Method.Get -> Root / "users" => BasicallyAuthorize("user", "password") ! GetUsers
}
}
```
15 changes: 15 additions & 0 deletions docs/demo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Demo

There is a single-file `demo` project , which is a complete REST API backend written with `finch-core` and `finch-json`
modules. The source code of the demo project is altered with useful comments that explain how to use its building blocks
such as `Endpoint`, `RequestReader`, `ResponseBuilder`, etc. The `demo` module is just a single Scala file Main.scala
that is worth reading.

The following command may be used to run the demo:

```bash
sbt 'project demo' 'run io.finch.demo.Main'
```

----
**#** [Endpoints](endpoint.md) | [Table Of Contents](index.md#table-of-contents)
40 changes: 40 additions & 0 deletions docs/endpoint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## Endpoints

One of the most powerful abstractions in Finch is an `Endpoint`, which is a composable router. At the high level
it might be treated as a usual `PartialFunction` from request to service. Endpoints may be converted to Finagle services.
And more importantly they can be composed with other building blocks like filters, services or endpoints itself.

The core operator in Finch is _pipe_ (bang) `!` operator, which is like a Linux pipe exposes the data flow. Both
requests and responses may be piped via chain building blocks (filters, services or endpoints) in exact way it has been
written.

The common sense of using the Finch library is to have an `Endpoint` representing the domain. For example, the typical
use case would be to have an `Endpoint` from `OAuth2Request` (see [OAuth2 section](docs.md#authorization-with-oauth2))
to `Json` (see section [Json](docs.md#finch-json)). Since, all the endpoints have the same type (i.e.,
`Endpoint[OAuth2Request, Json]`) they may be composed together into a single entry point with either `Endpoint.join()`
or `orElse` operators. The following example shows the discussed example in details:


```scala
val auth: Filter[HttpRequest, HttpResponse, OAuth2Request, HttpResponse] = ???
val users: Endpoint[OAuth2Request, Json] = ???
val groups: Endpoint[OAuth2Request, Json] = ???
val endpoint: Endpoint[OAuth2Request, HttpResponse] = (users orElse groups) ! TurnIntoHttp[Json]

// An HTTP endpoint that may be served with `Httpx`
val httpEndpoint: Endpoint[HttpRequest, HttpResponse] = auth ! endpoint
```

The best practices on what to choose for data transformation are following:

* Services should be used when the request is not required for the transformation.
* Otherwise, pure Finagle filters should be used.

An `Endpoint[Req, Rep]` may be implicitly converted into `Service[Req, Rep]` by importing the `io.finch._` object. Thus,
the following code is correct.

```scala
val e: Endpoint[MyRequest, HttpResponse] = ???
// an endpoint `e` will be converted to service implicitly
Httpx.serve(new InetSocketAddress(8081), e)
```
80 changes: 80 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<p align="center">
<img src="https://raw.githubusercontent.com/finagle/finch/master/finch-logo.png" width="360px" />
</p>

The Finch library provides an immutable layer of functions and types atop of [Finagle][1] for writing lightweight
consuming HTTP services. It roughly contains three packages: [io.finch.route](route.md), [io.finch.request](request.md),
[io.finch.response](response.md), which correspond to three simple steps to a robust REST/HTTP API:

#### Step 1: Routing the HTTP requests to a `Service`

The [Router](route.md#router) abstraction routes the requests depending on their path and method information. `Router`
combinator provides a bunch of predefined routers handling separated parts of a route. `Router`s might be composed with
either `/` (`flatMap`) or `/>` (`map`) operator. There is also `|` (`orElse`) operator that combines two routers in
terms of the inclusive or operator.

```scala
val router: Endpoint[HttpRequest, HttpResponse] = Get / ("users" | "user") / int /> GetUser
```

#### Step 2: Reading the HTTP requests in a `Service`

The [RequestReader](request.md#request-reader) abstraction is responsible for reading any details form the HTTP request.
`RequestReader` is composable in both ways: via the monadic API (using the for-comprehension, i.e., `flatMap`/`map`) and
via the applicative API (using the `~` operator). These approaches define an unlimited number of readers out the plenty
of predefined ones.

```scala
val pagination: RequestReader[(Int, Int)] =
OptionalParam("offset").as[Int] ~ OptionalParam("limit").as[Int] map {
case offset ~ limit => (offset.getOrElse(0), limit.getOrElse(100))
}
```

#### Step 3: Building the HTTP responses in a `Service`

The [ResponseBuilder](response.md#response-builder) abstraction provides a convenient way of building the HTTP responses
of any type. In fact, `ResponseBuilder` is a function that takes some content and builds an HTTP response of a type
depending on a content. There are plenty of predefined builders that might be used directly.

```scala
val ok: HttpResponse = Ok("Hello, world!") // plain/text HTTP response with status code 200
```

## Table of Contents

* [Demo](demo.md)
* [Endpoints](docs.md#endpoints)
* [Requests](docs.md#requests)
* [Custom Request Types](docs.md#custom-request-types)
* [Request Reader](docs.md#request-reader)
* [Overview](docs.md#overview)
* [API](docs.md#api)
* [Base Readers](docs.md#base-readers)
* [Required and Optional Readers](docs.md#required-and-optional-readers)
* [Multi-Value Parameters](docs.md#multi-value-parameters)
* [Custom Readers](docs.md#custom-readers)
* [Error Handling](docs.md#error-handling)
* [Combining and Reusing Readers](docs.md#combining-and-reusing-readers)
* [Applicative Syntax](docs.md#applicative-syntax)
* [Monadic Syntax](docs.md#monadic-syntax)
* [Type Conversion](docs.md#type-conversion)
* [Built-in Decoders](docs.md#built-in-decoders)
* [Custom Decoders](docs.md#custom-decoders)
* [Integration with JSON Libraries](docs.md#integration-with-json-libraries)
* [Validation](docs.md#validation)
* [Inline Validation](docs.md#inline-validation)
* [Reusable Rules](docs.md#reusable-validators)
* [Built-in Rules](docs.md#built-in-rules)
* [Responses](docs.md#responses)
* [Response Builder](docs.md#response-builder)
* [HTTP Redirects](docs.md#redirects)
* [Authentication](docs.md#authentication)
* [OAuth2](docs.md#authorization-with-oauth2)
* [Basic Auth](docs.md#basic-http-auth)
* [JSON](docs.md#json)
* [Finch Json](docs.md#finch-json)
* [Argonaut](docs.md#argonaut)
* [Jawn](docs.md#jawn)

[1]: http://twitter.github.io/finagle/
47 changes: 47 additions & 0 deletions docs/json.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
## JSON

### Finch-JSON

The Finch library is shipped with an immutable JSON API: `finch-json`, an extremely lightweight and simple
implementation of JSON: [Json.scala][3]. The usage looks as follows:

```scala
import io.finch.json._
import io.finch.request._

vaj j: Json = Json.obj("a" -> 10)
val i: RequestReader[Json] = RequiredBody.as[Json]
val o: HttpResponse = Ok(Json.arr("a", "b", "c")
```

### Argonaut

The `finch-argonaut` module provides the support for the [Argonaut][4] JSON library.

### Jawn

The `finch-jawn` module provides the support for [Jawn][5].

To decode a string with Jawn, you need to import `io.finch.jawn._` and define any Jawn `Facade` as an `implicit val`.
Using either `RequiredJsonBody` or `OptionalJsonBody` will take care of the rest.

To encode a value from the Jawn AST (specifically a `JValue`), you need only import `io.finch.jawn._` and
the `Encoder` will be imported implicitly.

### Jackson

The `finch-jackson` module provides the support for the [Jackson][6] library. The library usage looks as follows:

```scala
import io.finch.jackson._
implicit val objectMapper: ObjectMapper = new ObjectMapper().registerModule(DefaultScalaModule)
case class Foo(id: Int, s: String)

val ok: HttpResponse = Ok(Foo(10, "foo")) // will be encoded as JSON
val foo: RequestReader[Foo] = RequiredBody.as[Foo] // a request reader that reads Foo
```

[3]: https://github.com/finagle/finch/blob/master/finch-json/src/main/scala/io/finch/json/Json.scala
[4]: http://argonaut.io
[5]: https://github.com/non/jawn
[6]: http://jackson.codehaus.org/

0 comments on commit 6c43fb6

Please sign in to comment.