Generate Swagger / OpenAPI specification from scala source code (http4s)
Asked Answered
P

3

9

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: []
Papa answered 21/9, 2017 at 11:41 Comment(0)
P
5

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'

Git source: https://github.com/http4s/rho/blob/0c5aed48aeeea18b1d66d88b58cd3deea733f070/swagger/src/main/scala/org/http4s/rho/swagger/SwaggerSupport.scala#L30

Papa answered 21/9, 2017 at 12:39 Comment(2)
such a important component that oddly there's no document about it.Obliterate
Example code of Http4s/Rho's Swagger output can be found at github.com/http4s/rho/tree/master/examples/src/main/scala/com/…Uranian
L
7

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.

Lampkin answered 26/2, 2019 at 10:11 Comment(1)
Interesting. I will look at it for a next projectPapa
P
5

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'

Git source: https://github.com/http4s/rho/blob/0c5aed48aeeea18b1d66d88b58cd3deea733f070/swagger/src/main/scala/org/http4s/rho/swagger/SwaggerSupport.scala#L30

Papa answered 21/9, 2017 at 12:39 Comment(2)
such a important component that oddly there's no document about it.Obliterate
Example code of Http4s/Rho's Swagger output can be found at github.com/http4s/rho/tree/master/examples/src/main/scala/com/…Uranian
C
1

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

}
Chufa answered 7/1, 2022 at 11:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.