Reputation: 2899
So I'm no swagger expert, but all systems using swagger require you to have the swagger specification in JSON or YAML defining all endpoints (and such) of your API.
My question is: Are there know ways to generate these specification files based on the actual source code? I'm asking, because it seems very hard to keep endpoint code & documentation in sync when you start adding properties or returning slightly different results.
So when I have this code (using http4s & RhoService):
object HelloWorld {
val service = new RhoService {
GET / "hello" / 'name |>> { name: String =>
Ok(Json.obj("message" -> Json.fromString(s"Hello, ${name}")))
}
}
}
It would be great if it could produce (in some way:)
/hello/{name}:
get:
tags:
- "HelloWorld"
summary: ""
description: ""
operationId: "getHellobyName"
produces:
- "application/json"
parameters:
- name: "name"
in: "path"
description: ""
required: true
type: "string"
responses:
200:
description: "successful operation"
schema:
$ref: "#/definitions/Hello"
security:
- api_key: []
Upvotes: 9
Views: 5889
Reputation: 9663
Disclaimer: I’m the author of endpoints4s.
Similar to tapir (mentioned in another answer) endpoints4s is a library that can produce http4s servers and OpenAPI documentation for HTTP endpoints.
You would write your example like the following:
// --- This is the description of your HTTP service
import endpoints4s.algebra
case class Hello(message: String)
trait HelloWorldEndpoints
extends algebra.Endpoints
with algebra.JsonEntitiesFromSchemas {
implicit val helloJsonSchema: JsonSchema[Hello] =
field[String]("message")
.xmap(message => Hello(message))(hello => hello.message)
val hello: Endpoint[String, Hello] =
endpoint(
get(path / "hello" / segment[String]("name")),
ok(jsonResponse[Hello])
)
}
// --- This is an OpenApi documentation of the endpoints defined
// --- in HelloWorldEndpoints
import endpoints4s.openapi
import endpoints4s.openapi.model.{ Info, OpenApi }
object HelloWorldDocumentation
extends HelloWorldEndpoints
with openapi.Endpoints
with openapi.JsonEntitiesFromSchemas {
val api: OpenApi =
openApi(Info(title = "Hello API", version = "1.0"))(
hello
)
}
// --- This is an http4s server that implements the endpoints defined
// --- in HelloWorldEndpoints
import endpoints4s.http4s
import cats.effect.IO
import org.http4s.HttpRoutes
object HelloWorld
extends http4s.server.Endpoints[IO]
with http4s.server.JsonEntitiesFromSchemas
with HelloWorldEndpoints {
val service: HttpRoutes[IO] = HttpRoutes.of(
routesFromEndpoints(
hello.implementedBy(name => Hello(s"Hello, ${name}"))
)
)
}
// --- And this is an http4s service that publishes the OpenApi documentation
object OpenApiServer
extends http4s.server.Endpoints[IO]
with http4s.server.JsonEntitiesFromEncodersAndDecoders {
val openApiService: HttpRoutes[IO] = HttpRoutes.of(
routesFromEndpoints(
endpoint(
get(path / "open-api.json"),
ok(jsonResponse[OpenApi])
).implementedBy(_ => HelloWorldDocumentation.api)
)
)
}
Upvotes: 1
Reputation: 2899
It is not documented well, but apparently http4s' RhoService
adds middleware to generate a swagger.json
based on your routes:
Fetch it by calling 'http://localhost:8080/swagger.json'
Upvotes: 5
Reputation: 8606
Disclaimer: I'm the author of tapir.
rho is one possibility. Another approach is to completely separate the description of the API endpoint from the business logic.
Having a description of an endpoint, which is a regular Scala value, it can be then interpreted as either a server (given the "business logic" function), or documentation.
Two Scala libraries that can provide both http4s and OpenAPI interpreters are tapir and typeapi.
Upvotes: 7