Last active
May 25, 2024 08:39
-
-
Save dacr/ec201da5fe3acc3e9f51dbc3e73230b1 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/c2f6303cd7dc5719bb533664fce41e5f60fbc5db
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 : 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.4.2" | |
//> 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