-
-
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
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
// 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