-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #167 from buildo/166-add_documentation_to_tapiro
- Loading branch information
Showing
9 changed files
with
285 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
--- | ||
id: installation | ||
title: Installation | ||
--- | ||
|
||
`tapiro` can be installed as an Sbt plugin. | ||
|
||
`sbt-tapiro` is an Sbt plugin that uses `tapiro` to generate http/json routes parsing scala traits definitions. | ||
|
||
## Installation | ||
|
||
To start using `sbt-tapiro` simply add this line in `project/plugins.sbt` | ||
|
||
```scala | ||
addSbtPlugin("io.buildo" %% "sbt-tapiro" % "@SBT_TAPIRO_STABLE_VERSION@") | ||
``` | ||
|
||
### Snapshot releases | ||
|
||
We publish a snapshot version on every merge on master. | ||
|
||
The latest snapshot version is `@SBT_TAPIRO_SNAPSHOT_VERSION@` and you can use | ||
it to try the latest unreleased features. For example: | ||
|
||
```scala | ||
addSbtPlugin("io.buildo" %% "sbt-tapiro" % "@SBT_TAPIRO_SNAPSHOT_VERSION@") | ||
resolvers += Resolver.sonatypeRepo("snapshots") | ||
``` | ||
|
||
## Plugin | ||
|
||
To use the code generator, you need to add this to your `build.sbt`. | ||
|
||
```scala | ||
import _root_.io.buildo.tapiro.Server | ||
|
||
lazy val application = project | ||
.settings( | ||
libraryDependencies ++= applicationDependencies ++ tapiroDependencies, | ||
tapiro / tapiroRoutesPaths := List("[path to routes]"), | ||
tapiro / tapiroModelsPaths := List("[path to models]"), | ||
tapiro / tapiroOutputPath := "[path to endpoints]", | ||
tapiro / tapiroEndpointsPackages := List("[package]", "[subpackage]"), | ||
tapiro / tapiroServer := Server.AkkaHttp, //or Server.Http4s | ||
) | ||
.enablePlugins(SbtTapiro) | ||
``` | ||
|
||
You can now run it with `sbt application/tapiro`. | ||
|
||
```scala | ||
## Dependencies | ||
|
||
The generated code comes with library dependencies. | ||
|
||
In case akka-http version is used: | ||
```scala | ||
val V = new { | ||
val circe = "@CIRCE_VERSION@" | ||
val tapir = "@TAPIR_VERSION@" | ||
val akkaHttp = "@AKKA_HTTP_VERSION@" | ||
} | ||
|
||
val tapiroDependencies = Seq( | ||
"com.softwaremill.sttp.tapir" %% "tapir-json-circe" % V.tapir, | ||
"com.softwaremill.sttp.tapir" %% "tapir-core" % V.tapir, | ||
"com.softwaremill.sttp.tapir" %% "tapir-akka-http-server" % V.tapir, | ||
"com.typesafe.akka" %% "akka-http" % V.akkaHttp, | ||
"io.circe" %% "circe-core" % V.circe, | ||
) | ||
``` | ||
|
||
In case http4s is used: | ||
|
||
```scala | ||
val V = new { | ||
val circe = "@CIRCE_VERSION@" | ||
val tapir = "@TAPIR_VERSION@" | ||
} | ||
|
||
val tapiroDependencies = Seq( | ||
"com.softwaremill.sttp.tapir" %% "tapir-json-circe" % V.tapir, | ||
"com.softwaremill.sttp.tapir" %% "tapir-core" % V.tapir, | ||
"com.softwaremill.sttp.tapir" %% "tapir-http4s-server" % V.tapir, | ||
"io.circe" %% "circe-core" % V.circe, | ||
) | ||
``` | ||
|
||
These dependencies usually go under `project/Dependencies.scala` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
--- | ||
id: introduction | ||
title: Introduction | ||
--- | ||
|
||
Tapiro parses your Scala controllers to generate HTTP endpoints. | ||
|
||
A Scala controller is a trait defined as follows: | ||
|
||
```scala mdoc | ||
import scala.annotation.StaticAnnotation | ||
|
||
class query extends StaticAnnotation | ||
class command extends StaticAnnotation | ||
|
||
case class Cat(name: String) | ||
case class Error(msg: String) | ||
|
||
trait Cats[F[_], AuthToken] { | ||
@query //translate this to a GET | ||
def findCutestCat(): F[Either[Error, Cat]] | ||
|
||
@command //translate this to a POST | ||
def doSomethingWithTheCat(catId: Int, token: AuthToken): F[Either[Error, Unit]] | ||
} | ||
``` | ||
|
||
For each controller tapiro generates two files: | ||
- `CatsEndpoints.scala` containing the HTTP api description using https://tapir-scala.readthedocs.io/ | ||
- `CatsHttp4sEndpoints.scala` or `CatsAkkaHttpEndpoints.scala` depeneding on the HTTP server the user is using. | ||
|
||
## Complete Example | ||
|
||
Here you have an example implementation of the `Cats` controller definied in the previous section: | ||
|
||
```scala mdoc | ||
import cats.effect._ | ||
|
||
object Cats { | ||
def create[F[_]](implicit F: Sync[F]) = new Cats[F, String] { | ||
override def findCutestCat(): F[Either[Error, Cat]] = | ||
F.delay(Right(Cat("Cheshire"))) | ||
override def doSomethingWithTheCat(catId: Int, token: String): F[Either[Error, Unit]] = | ||
F.delay(Right(())) | ||
} | ||
} | ||
|
||
|
||
``` | ||
|
||
Here you have the autogenerated magic fromo tapiro (This is the content of `CatsHttp4sEndpoints.scala` it will be autogenerated). | ||
|
||
```scala mdoc | ||
import org.http4s.HttpRoutes | ||
|
||
// ---- begins autogenerated code | ||
object CatsHttp4sEndpoints { | ||
def routes(controller: Cats[IO, String]): HttpRoutes[IO] = ??? | ||
} | ||
// ---- ends autogenerated code | ||
``` | ||
|
||
Here is how to run the server: | ||
|
||
```scala mdoc | ||
import org.http4s.server.blaze._ | ||
import org.http4s.implicits._ | ||
import cats.implicits._ | ||
|
||
object Main extends IOApp { | ||
val catsImpl = Cats.create[IO] | ||
val routes = CatsHttp4sEndpoints.routes(catsImpl) | ||
|
||
override def run(args: List[String]): IO[ExitCode] = | ||
BlazeServerBuilder[IO] | ||
.bindHttp(8080, "localhost") | ||
.withHttpApp(routes.orNotFound) | ||
.serve | ||
.compile | ||
.drain | ||
.as(ExitCode.Success) | ||
} | ||
``` | ||
|
||
The resulting server can be queried as follows: | ||
``` | ||
/GET /Cats/findCutestCat | ||
/POST /Cats/doSomethingWithTheCat -d '{ "catId": 1 }' | ||
``` | ||
|
||
## Authentication | ||
|
||
An `AuthToken` type argument is expected in each controller and is added as authorization header. | ||
|
||
`trait Cats[F[_], AuthToken]` | ||
|
||
The actual implementation of the `AuthToken` is left to the user. All tapiro requires is a proper tapir `PlainCodec` such as: | ||
|
||
```scala mdoc | ||
import sttp.tapir._ | ||
import sttp.tapir.Codec._ | ||
|
||
case class CustomAuth(token: String) | ||
|
||
def decodeAuth(s: String): DecodeResult[CustomAuth] = { | ||
val TokenPattern = "Token token=(.+)".r | ||
s match { | ||
case TokenPattern(token) => DecodeResult.Value(CustomAuth(token)) | ||
case _ => DecodeResult.Error(s, new Exception("token not found")) | ||
} | ||
} | ||
|
||
def encodeAuth(auth: CustomAuth): String = auth.token | ||
|
||
implicit val authCodec: PlainCodec[CustomAuth] = Codec.stringPlainCodecUtf8 | ||
.mapDecode(decodeAuth)(encodeAuth) | ||
``` | ||
|
||
The user will find the decoded token as the last argument of the method in the trait. | ||
|
||
```scala | ||
@command //translate this to a POST | ||
def doSomethingWithTheCat(catId: Int, token: AuthToken): F[Either[Error, Unit]] | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
--- | ||
id: migrate | ||
title: Migration from Wiro | ||
--- | ||
|
||
Tapiro is meant to deprecate [wiro](https://github.com/buildo/wiro). | ||
|
||
Tapiro is based on the same concepts of wiro, the migration is pretty straightforward. | ||
|
||
Here is a checklist of what you need to do: | ||
1. Install the plugin (as described in the [guide](installation.md)) | ||
2. Configure your `build.sbt` (as described in the [guide](installation.md)) | ||
3. Add `AuthToken` type parameter to controllers | ||
`trait AccountController[F]` -> `trait AccountController[F[_], AuthToken]` | ||
4. Modify controllers so that wiro `Auth` is replaced with AuthToken and move as last argument | ||
`def read(token: Auth, arg: Int)` -> `def read(arg: Int, token: AuthToken)` | ||
5. Add `**/*Endpoints.scala linguist-generated` to repository's `.gitattributes` to automatically collapse tapiro generated code in GitHub diffs | ||
6. Add required codecs | ||
This is a valid codec for wiro.Auth: | ||
|
||
```scala mdoc | ||
import sttp.tapir._ | ||
import sttp.tapir.Codec._ | ||
|
||
case class Auth(token: String) //should be imported as wiro.Auth instead | ||
|
||
implicit val authCodec: PlainCodec[Auth] = Codec.stringPlainCodecUtf8 | ||
.mapDecode(decodeAuth)(encodeAuth) | ||
|
||
def decodeAuth(s: String): DecodeResult[Auth] = { | ||
val TokenPattern = "Token token=(.+)".r | ||
s match { | ||
case TokenPattern(token) => DecodeResult.Value(Auth(token)) | ||
case _ => DecodeResult.Error(s, new Exception("token not found")) | ||
} | ||
} | ||
|
||
def encodeAuth(auth: Auth): String = auth.token | ||
``` | ||
7. Run `sbt tapiro` | ||
|
||
Using `Server.AkkaHttp` the resulting routes can be added to wiro as custom routes. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
id: rpc | ||
title: Why? | ||
--- | ||
|
||
> 📖 **NOTE**: Long time ago we wrote a [blogpost](https://blog.buildo.io/http-routes-at-buildo-1424250c41d3) about this. The blogpost is about [another library](https://github.com/buildo/wiro) but the underlying concepts are the same. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters