Skip to content

Instantly share code, notes, and snippets.

@scalway
Forked from dacr/tapir-zio-dynamic-api.sc
Created April 15, 2024 10:36
Show Gist options
  • Save scalway/805d93d0d64c9eeac9e7e0e9088f97af to your computer and use it in GitHub Desktop.
Save scalway/805d93d0d64c9eeac9e7e0e9088f97af to your computer and use it in GitHub Desktop.
An API to rule them all - to define or customize new API at runtime / published by https://github.com/dacr/code-examples-manager #dbccfc26-c532-4aa9-b3cd-eb13cbaa5370/764dc89bc8faf304aa6e1a5394904ae8cca6d0e4
// summary : An API to rule them all - to define or customize new API at runtime
// keywords : scala, zio, tapir, http, zhttp, stateful, state, @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 : dbccfc26-c532-4aa9-b3cd-eb13cbaa5370
// created-on : 2023-12-08T18:45:32+01: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.3.1"
//> using dep "com.softwaremill.sttp.tapir::tapir-zio:1.9.4"
//> using dep "com.softwaremill.sttp.tapir::tapir-zio-http-server:1.9.4"
//> using lib "com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.9.4"
// ---------------------
import sttp.tapir.ztapir.*
import sttp.tapir.EndpointIO.Example
import sttp.tapir.server.ziohttp.ZioHttpInterpreter
import sttp.apispec.openapi.Info
import sttp.tapir.swagger.bundle.SwaggerInterpreter
import zio.*, zio.http.Server
/*
curl -L http://127.0.0.1:8080/docs
curl http://127.0.0.1:8080/hello/david
curl http://127.0.0.1:8080/hello/joe
*/
object WebApp extends ZIOAppDefault {
case class AppState(
serversFibers: Map[Int, Fiber[Throwable, Nothing]] = Map.empty
) {
def manage(serverId: Int, serverFiber: Fiber[Throwable, Nothing]): AppState =
copy(serversFibers = serversFibers + (serverId -> serverFiber))
}
type WebAppEnv = Ref[AppState]
// --------------------------------------------------
val greetingExamples = List(Example.of("Hello"), Example.of("Hi"), Example.of("Good morning"))
val startEndPoint =
endpoint
.description("Start a new customized API")
.get
.in("start")
.in(path[String]("greeting").examples(greetingExamples))
.in(path[Int]("port").example(8081))
.out(emptyOutput)
val startRoute = startEndPoint.zServerLogic[WebAppEnv]((greeting, port) => buildDynamicServer(greeting, port).logError.ignore)
val apiRoutes = List(startRoute)
val docRoutes =
SwaggerInterpreter()
.fromServerEndpoints(
apiRoutes,
Info(title = "An API to define and control new API", version = "1.0")
)
// --------------------------------------------------
def buildDynamicServer(greetingToUse: String, port: Int) = {
val dynamicEndPoint =
endpoint
.description(s"Dynamically defined $greetingToUse")
.get
.in(s"greeting")
.in(path[String]("name").example("joe"))
.out(stringBody)
val dynamicRoute = dynamicEndPoint.zServerLogic[WebAppEnv](_ => ZIO.succeed(greetingToUse))
val dynamicDocRoutes =
SwaggerInterpreter()
.fromServerEndpoints(
List(dynamicRoute),
Info(title = "Dynamic generated API", version = "1.0")
)
val dynamicRoutes = ZioHttpInterpreter().toHttp(List(dynamicRoute) ++ dynamicDocRoutes)
for {
_ <- ZIO.logInfo(s"NEW API available http://127.0.0.1:$port/docs")
serverFiber <- Server
.serve(dynamicRoutes)
.provideSomeLayer(Server.defaultWithPort(port))
.forkDaemon
ref <- ZIO.service[Ref[AppState]]
state <- ref.updateAndGet(state => state.manage(port, serverFiber))
} yield ()
}
// --------------------------------------------------
val routes = ZioHttpInterpreter().toHttp(apiRoutes ++ docRoutes)
// --------------------------------------------------
val appStateLayer = ZLayer(Ref.make(AppState()))
val defaultPort = 8080
override def run =
ZIO.logInfo(s"META API available http://127.0.0.1:$defaultPort/docs") *>
Server
.serve(routes)
.provide(appStateLayer, Server.default)
}
WebApp.main(Array.empty)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment