Skip to content

Commit

Permalink
Merge pull request #1114 from efossier/http4s-docs
Browse files Browse the repository at this point in the history
[WIP] Add Http4s documentation to microsite
  • Loading branch information
blast-hardcheese authored Jul 4, 2021
2 parents 7d3b185 + 528d28a commit 8aeafff
Show file tree
Hide file tree
Showing 14 changed files with 406 additions and 20 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Useful commands inside sbt console
- `cli`: Useful for scripting: `sbt 'cli --client ...'`
- `format`: Runs scalafmt against codebase
- `checkFormatting`: Verifies formatting, run as part of CI against PRs
- `mdoc`: Generate the doc microsite locally

Resources
=========
Expand Down
6 changes: 3 additions & 3 deletions modules/microsite/docs/java/dropwizard/generating-a-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ As we saw in [Installation](installation), guardrail is run as part of maven. It

Lets take another look at the maven config for guardrail:

```xml
```xml
<plugin>
<groupId>com.twilio</groupId>
<artifactId>guardrail-maven-plugin_2.12</artifactId>
Expand All @@ -33,11 +33,11 @@ Lets take another look at the maven config for guardrail:
</execution>
</executions>
</plugin>
```
```

**NB**: if you prefer to use Vavr instead of Java's standard library collections, see the [generic Java instructions](../) for appropriate configuration options.

Once configured, you can manually invoke guardrail with `mvn generate-sources`.
Once configured, you can manually invoke guardrail with `mvn generate-sources`.

Server Handlers, Resources
--------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ As all parameters are provided as arguments to the function stubs in the trait,
Separation of business logic
----------------------------

Providing an implementating of a function with a well-defined set of inputs and outputs is natural for any developer. By reducing the scope of the interface a developer writes against, implementations are more clear and concise.
Providing an implementation of a function with a well-defined set of inputs and outputs is natural for any developer. By reducing the scope of the interface a developer writes against, implementations are more clear and concise.

Furthermore, by providing business logic as an implementation of an abstract class, unit tests can test the routing layer and business logic independently, by design.

Expand Down
4 changes: 2 additions & 2 deletions modules/microsite/docs/scala/akka-http/generating-clients.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ title: "Generating Clients - akka-http - scala - guardrail"
Generating clients
==================

As we've seen in [Generating a Server](generating-a-server), guardrail-generated servers establish a mapping between our business logic and a cordoned off a subset of HTTP. This permits us to focus on our business logic, without getting overloaded with the complexities of managing such a large protocol. The same is true with guardrail generated HTTP Clients: from a consumer's standpoint, HTTP calls should look like regular function calls, accepting domain-specific arguments and producing domain-specific results.
As we've seen in [Generating a Server](generating-a-server), guardrail-generated servers establish a mapping between our business logic and a cordoned off subset of HTTP. This permits us to focus on our business logic, without getting overloaded with the complexities of managing such a large protocol. The same is true with guardrail generated HTTP Clients: from a consumer's standpoint, HTTP calls should look like regular function calls, accepting domain-specific arguments and producing domain-specific results.

By generating minimal clients that only have enough business knowledge to map domain types to and from HTTP, opportunities for logical errors are effectively removed. While this does not eliminate logical errors entirely, establishing a firm boundary between the underlying protocol and hand-written code drastically reduces the scope of possible bugs.

Expand All @@ -28,7 +28,7 @@ DocsHelpers.renderScalaSnippet(AkkaHttp, GeneratingClients)(
Separation of protocol-concerns from API-level concerns
-------------------------------------------------------

As guardrail clients are built ontop of the function type `HttpRequest => Future[HttpResponse]`, client configuration is reduced to function composition. Some ideas:
As guardrail clients are built on top of the function type `HttpRequest => Future[HttpResponse]`, client configuration is reduced to function composition. Some ideas:

```scala
val singleRequestHttpClient = { (req: HttpRequest) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ guardrail has [a number of vendor extensions](https://github.com/twilio/guardrai
An arbitrary class name prefix that overrides the default class name
when generating JVM code. See also <code>x-scala-class-prefix</code>.
</td>
</tr>
</tr>
</tbody>
</table>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ paths: # All HTTP paths are direct children
# parameter name.

in: path # Where to look for the parameter

description: The ID of the user # The optional `description` parameter is not used in guardrail,
# but is useful for providing a detailed explanation on what is
# expected as a value for the parameter. For example:
# `description: User IDs are strings comprised of the concatenation
# expected as a value for the parameter. For example:
# `description: User IDs are strings comprised of the concatenation
# of the two upper-case letters ID and a UUID stripped of any dashes
# i.e. ID4d9b1c54e4664c9d92aba94151a7f59f`

Expand Down
61 changes: 61 additions & 0 deletions modules/microsite/docs/scala/http4s/generating-a-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
layout: docs
title: "Generating a Server - http4s - scala - guardrail"
---

Generating a Server
===================

guardrail-generated servers come in two parts: a `Resource` and a `Handler`. The `Resource` contains all the routing logic, accepting a `Handler` as an argument to the `route` function in order to provide an HTTP service in whichever supported HTTP framework you're hosting your service in.

The following is an example from the [http4s](https://github.com/http4s/http4s) server generator:

```scala mdoc:passthrough
import com.twilio.guardrail.generators.Scala.Http4s
import com.twilio.guardrail.docs._
DocsHelpers.renderScalaSnippet(Http4s, GeneratingAServer)("""
|// The `Handler` trait is fully abstracted from the underlying http framework. As a result, with the exception of some
|// structural alterations (`F[_]` instead of `Future[_]` as the return type) the same handlers can be used with
|// different `Resource` implementations from different framework generators. This permits greater compatibility between
|// different frameworks without changing your business logic.
""".stripMargin,
""
)
```

As all parameters are provided as arguments to the function stubs in the trait, there's no concern of forgetting to extract a query string parameter, introducing a typo in a form parameter name, or forgetting to close the bytestream for the streaming HTTP Request.

The routes and resources generated by guardrail can be hooked up into your HTTP4s server like so:

```scala
val usersApi = new UsersApi() // Class which implements the generated UsersHandler from guardrail
val usersService = new UsersResource[IO]().routes(usersApi)
val httpApp = Router("/" -> usersService).orNotFound

// Same basic server setup as in the http4s quickstart
BlazeServerBuilder[IO]
.bindHttp(5000, "localhost")
.withHttpApp(httpApp)
.resource
.use(_ => IO.never)
.as(ExitCode.Success)
```

Separation of business logic
----------------------------

Providing an implementation of a function with a well-defined set of inputs and outputs is natural for any developer. By reducing the scope of the interface a developer writes against, implementations are more clear and concise.

Furthermore, by providing business logic as an implementation of an abstract class, unit tests can test the routing layer and business logic independently, by design.

API structure slip is impossible
--------------------------------

As parameters are explicitly provided as arguments to functions in `Handler`s, any alteration to parameters constitute a new function interface that must be implemented. As a result, if providing an implementation for an externally managed specification, the compiler informs when a previously written function is no longer sufficient.

By representing different response codes and structures as members of a sealed trait, it's impossible to return a structure that violates the specification, even for less frequently used response codes.

Finally, describing an endpoint in your specification without providing an implementation for it is a compiler error. This prevents reduction of functionality due to refactors, human error, or miscommunication with other teams.

<span style="float: left">[Prev: Sample API specification](sample-api-specification)</span>
<span style="float: right">[Next: Generating clients](generating-clients)</span>
35 changes: 35 additions & 0 deletions modules/microsite/docs/scala/http4s/generating-clients.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
layout: docs
title: "Generating Clients - http4s - scala - guardrail"
---

Generating clients
==================

As we've seen in [Generating a Server](generating-a-server), guardrail-generated servers establish a mapping between our business logic and a cordoned off subset of HTTP. This permits us to focus on our business logic, without getting overloaded with the complexities of managing such a large protocol. The same is true with guardrail generated HTTP Clients: from a consumer's standpoint, HTTP calls should look like regular function calls, accepting domain-specific arguments and producing domain-specific results.

By generating minimal clients that only have enough business knowledge to map domain types to and from HTTP, opportunities for logical errors are effectively removed. While this does not eliminate logical errors entirely, establishing a firm boundary between the underlying protocol and hand-written code drastically reduces the scope of possible bugs.

The following is an example from the [http4s](https://github.com/http4s/http4s) client generator:

```scala mdoc:passthrough
import com.twilio.guardrail.generators.Scala.Http4s
import com.twilio.guardrail.docs._
DocsHelpers.renderScalaSnippet(Http4s, GeneratingClients)(
"""|// Two constructors are provided, one accepting the `httpClient` and `Async`
|// implicitly, the other accepting an explicit `httpClient`, but still
|// accepting the `Async` implicitly
""".stripMargin,
""
)
```

Separation of protocol-concerns from API-level concerns
-------------------------------------------------------

As guardrail clients are built on top of any Http4s client type, client configuration is done the same way as you are
already familiar with when using Http4s.

Check out the docs for [Http4s Clients](https://http4s.org/v0.20/client/).

<span style="float: left">[Prev: Generating a Server](generating-a-server)</span>
22 changes: 17 additions & 5 deletions modules/microsite/docs/scala/http4s/index.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
---
layout: docs
title: "guardrail[http4s <- scala]"
title: "http4s - scala - guardrail"
---

These docs are under construction!
Table of Contents
=================

In the meantime, please refer to the [akka-http](../akka-http) docs for broad context and getting started.

Note that the `framework` for http4s is `"http4s"`, so using that in your SBT, Gradle, or Maven plugin configuration should get you started.
1. [What is guardrail](what-is-guardrail.md)
1. [Single Point of Truth](what-is-guardrail.md#single-point-of-truth)
1. [Unexpected API changes are compiler errors](what-is-guardrail.md#unexpected-api-changes-are-compiler-errors)
1. [Fewer binary dependencies](what-is-guardrail.md#fewer-binary-dependencies)
1. [Installation](installation)
1. [Sample API specification](sample-api-specification)
1. [Generating a Server](generating-a-server)
1. [Separation of business logic](generating-a-server#separation-of-business-logic)
1. [API structure slip is impossible](generating-a-server#api-structure-slip-is-impossible)
1. [Generating test-only (real) server mocks for unit tests](generating-a-server#generating-test-only-real-server-mocks-for-unit-tests)
1. [A note about scalatest integration](generating-a-server#a-note-about-scalatest-integration)
1. [Generating clients](generating-clients)
1. [Separation of protocol-concerns from API-level concerns](generating-clients#separation-of-protocol-concerns-from-api-level-concerns)
1. [guardrail Extensions](guardrail-extensions)
35 changes: 35 additions & 0 deletions modules/microsite/docs/scala/http4s/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
layout: docs
title: "Installation - http4s - scala - guardrail"
---

Installation
============

guardrail is available as a modular core, with both [sbt](https://github.com/twilio/sbt-guardrail) and [Maven](https://github.com/twilio/guardrail-maven-plugin) integration. The core can also be run as a stand-alone [CLI](https://github.com/twilio/guardrail/blob/978a92db3dd46812aa19f05050995f864cbb5bb3/build.sbt#L33-L48) application, with full support for all features.

To generate servers or clients using the `http4s` framework, set `http4s` as the framework in the generation configuration in either sbt or maven.

If compiling with Scala < 2.13.x, you'll need to enable `-Ypartial-unification`:

```scala
scalacOptions += "-Ypartial-unification"
```

If compiling with Scala < 2.12.x, you'll additionally need the `-Xexperimental` flag:

```scala
scalacOptions += "-Xexperimental"
```

Additionally, you will need to manually include dependencies in your project for the following packages:
- `http4s`, dsl, server, and client dependencies
- `http4s-circe` for JSON decoding and encoding support
- `circe-generic` for JSON decoding and encoding support
- `cats-effect` for http4s integration
- `cats-core` for http4s integration

Versions of these libraries should be picked by checking out the [Compatibility Matrix](https://github.com/guardrail-dev/guardrail/blob/master/COMPATIBILITY.md).

<span style="float: left">[Prev: What is guardrail?](what-is-guardrail)</span>
<span style="float: right">[Next: Sample API specification](sample-api-specification)</span>
Loading

0 comments on commit 8aeafff

Please sign in to comment.