Skip to content

Instantly share code, notes, and snippets.

@dacr
Created June 15, 2024 07:58
Show Gist options
  • Save dacr/62d5760356927be012449fbfad39a1f0 to your computer and use it in GitHub Desktop.
Save dacr/62d5760356927be012449fbfad39a1f0 to your computer and use it in GitHub Desktop.
tapir with iron for data validity and better documentation / published by https://github.com/dacr/code-examples-manager #c100bdc5-c628-4aa7-86f3-f838a07a6266/d1df5513fa32f68df76ca706304d0a3d71cae9d7
// summary : tapir with iron for data validity and better documentation
// keywords : scala, zio, tapir, iron, @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 : c100bdc5-c628-4aa7-86f3-f838a07a6266
// created-on : 2024-06-06T13:37:39+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-iron: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 "io.github.iltotore::iron-zio:2.5.0"
//> using dep "io.github.iltotore::iron-zio-json:2.5.0"
// ---------------------
import zio.*
import zio.json.*
import zio.http.Server
import io.github.iltotore.iron.*
import io.github.iltotore.iron.constraint.all.*
import io.github.iltotore.iron.zioJson.given
import sttp.tapir.ztapir.*
import sttp.tapir.server.ziohttp.ZioHttpInterpreter
import sttp.tapir.swagger.bundle.SwaggerInterpreter
import sttp.tapir.json.zio.*
import sttp.tapir.Schema
import sttp.tapir.generic.auto.*
import sttp.tapir.codec.iron.*
import sttp.apispec.openapi.Info
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 :| Not[Empty] :| Alphanumeric,
age: Option[Int :| Positive] /*DescribedAs "Age should be positive"*/,
gender: Option[Gender],
nicknames: List[String :| (MinLength[3] & MaxLength[6])] :| Not[Empty]
)
object Salute {
given JsonEncoder[Salute] = DeriveJsonEncoder.gen
given JsonDecoder[Salute] = DeriveJsonDecoder.gen
}
case class Greeting(message: String) derives JsonCodec
// --------------------------------------------------
def helloLogic(salute: Salute): UIO[Greeting] = {
val nicknames:List[String] = salute.nicknames
for {
nickName <- Random.shuffle(nicknames).map(_.headOption) // .when(!salute.nicknames.isEmpty)
name = nickName.getOrElse(salute.name)
message = salute match {
case Salute(name, Some(foundAge:Int), _, _) 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