Created
June 15, 2024 07:58
-
-
Save dacr/9c8767572f9ad8311b744e9a9cf76935 to your computer and use it in GitHub Desktop.
tapir with refined for data validity and better documentation / published by https://github.com/dacr/code-examples-manager #7c39c8b8-68a8-4204-be91-062447bc25ac/1ac996a8340e44b5a4681249bf68c2f23f21f7e4
This file contains hidden or 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
// summary : tapir with refined for data validity and better documentation | |
// keywords : scala, zio, tapir, refined, @testable, @exclusive | |
// publish : gist | |
// authors : David Crosson | |
// license : Apache NON-AI License Version 2.0 (https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-APACHE2) | |
// id : 7c39c8b8-68a8-4204-be91-062447bc25ac | |
// created-on : 2024-06-06T08:50:06+02:00 | |
// managed-by : https://github.com/dacr/code-examples-manager | |
// run-with : scala-cli $file | |
// test-with : curl -L http://127.0.0.1:8080/hello/david | |
// --------------------- | |
//> using scala "3.4.2" | |
//> using dep "com.softwaremill.sttp.tapir::tapir-zio:1.10.9" | |
//> using dep "com.softwaremill.sttp.tapir::tapir-zio-http-server:1.10.9" | |
//> using dep "com.softwaremill.sttp.tapir::tapir-refined:1.10.9" | |
//> using dep "com.softwaremill.sttp.tapir::tapir-json-zio:1.10.9" | |
//> using dep "com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.10.9" | |
//> using dep "dev.zio::zio-json-interop-refined:0.7.0" | |
// --------------------- | |
import sttp.tapir.ztapir.* | |
import sttp.tapir.server.ziohttp.ZioHttpInterpreter | |
import sttp.apispec.openapi.Info | |
import sttp.tapir.swagger.bundle.SwaggerInterpreter | |
import sttp.tapir.Schema.annotations.customise | |
import sttp.tapir.json.zio.* | |
import sttp.tapir.Schema | |
import sttp.tapir.generic.auto.* | |
import sttp.tapir.codec.refined.* | |
import eu.timepit.refined.* | |
import eu.timepit.refined.api.Refined | |
import eu.timepit.refined.auto.* | |
import eu.timepit.refined.boolean.And | |
import eu.timepit.refined.collection.{MaxSize, MinSize, NonEmpty} | |
import eu.timepit.refined.numeric.Positive | |
import zio.* | |
import zio.json.* | |
import zio.json.interop.refined.* | |
import zio.http.Server | |
object WebApp extends ZIOAppDefault { | |
enum Gender derives JsonCodec { | |
case Female, Male | |
} | |
object Gender { | |
given JsonEncoder[Gender] = JsonEncoder[String].contramap(p => p.toString) | |
given JsonDecoder[Gender] = JsonDecoder[String].map(p => Gender.valueOf(p)) | |
given Schema[Gender] = Schema.derivedEnumeration.defaultStringBased | |
} | |
case class Salute( | |
name: String Refined NonEmpty, | |
age: Option[Int Refined Positive], | |
gender: Option[Gender], | |
// nicknames: List[String], // https://github.com/softwaremill/tapir/issues/993 | |
nicknames: List[String Refined (MinSize[3] And MaxSize[6])] Refined NonEmpty | |
) derives JsonCodec | |
case class Greeting(message: String) derives JsonCodec | |
// -------------------------------------------------- | |
def helloLogic(salute: Salute): UIO[Greeting] = { | |
for { | |
nickName <- Random.shuffle(salute.nicknames.toList).map(_.headOption.map(_.value)) // .when(!salute.nicknames.isEmpty) | |
name = nickName.getOrElse(salute.name) | |
message = salute match { | |
case Salute(name, Some(foundAge), _, _) if foundAge < 18 => s"Hi $name" | |
case Salute(name, Some(foundAge), Some(Gender.Female), _) => s"Hello madam $name" | |
case Salute(name, Some(foundAge), Some(Gender.Male), _) => s"Hello mister $name" | |
case Salute(name, _, _, _) => s"Good morning $name" | |
} | |
} yield Greeting(message) | |
} | |
val helloEndPoint = | |
endpoint | |
.tag("Greetings") | |
.name("hello") | |
.description("Returns stateful greeting") | |
.post | |
.in("hello") | |
//.in(jsonBody[Salute].example(Salute("Joe", None, None, List("joe")))) | |
.in(jsonBody[Salute]) | |
.out(jsonBody[Greeting]) | |
val helloRoute = helloEndPoint.zServerLogic[Any](helloLogic) | |
// -------------------------------------------------- | |
val apiDocRoute = | |
SwaggerInterpreter() | |
.fromServerEndpoints( | |
List(helloRoute), | |
Info( | |
title = "Greeting API", | |
version = "1.0", | |
description = Some("Everything required to be polite") | |
) | |
) | |
// -------------------------------------------------- | |
val routes = ZioHttpInterpreter().toHttp(List(helloRoute) ++ apiDocRoute) | |
// -------------------------------------------------- | |
val server = for { | |
_ <- ZIO.log("API documentation : http://127.0.0.1:8080/docs") | |
_ <- Server.serve(routes) | |
} yield () | |
override def run = server.provide(Server.default) | |
} | |
WebApp.main(Array.empty) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment