Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate OpenAPI documentation from EndpointSpec data type #1498

Closed
jdegoes opened this issue Sep 18, 2022 · 18 comments · Fixed by #2470
Closed

Generate OpenAPI documentation from EndpointSpec data type #1498

jdegoes opened this issue Sep 18, 2022 · 18 comments · Fixed by #2470
Labels
💎 Bounty enhancement New feature or request 💰 Rewarded ziohackathon An official ticket for ZIO Hackathon

Comments

@jdegoes
Copy link
Member

jdegoes commented Sep 18, 2022

Is your feature request related to a problem? Please describe.

The zio.http.api.EndpointSpec contains a declarative description of an API endpoint, while the ServiceSpec data type contains a declarative description of more than one API endpoints.

These declarative descriptions are wholly sufficient to generate extremely good OpenAPI documentation for the endpoints. Yet, this is a feature that is wholly missing in the current version of zio.http.api.

Describe the solution you'd like

Add a method EndpointSpec#toOpenAPI, which translates the route, query parameter, header, input and output, into the zio.http.api.openapi.* family of data types, incorporating the custom documentation that may be included into the EndpointSpec or into one of the HttpCodec values that make up its description of the input.

Describe alternatives you've considered

N/A

Additional context

This ticket does not include translating the OpenAPI values into HTML or JSON, which is considered outside the scope of this ticket. This ticket is merely to translate the EndpointSpec data type into typed OpenAPI values.

A separate ticket will cover the generation of JSON (etc.) from the OpenAPI data types.

@jdegoes jdegoes added the enhancement New feature or request label Sep 18, 2022
@jdegoes jdegoes added the ziohackathon An official ticket for ZIO Hackathon label Oct 25, 2022
@iusildra
Copy link

I'll try to do this one

@jdegoes jdegoes changed the title Generate OpenAPI documentation from API data type Generate OpenAPI documentation from EndpointSpec data type Nov 22, 2022
@jdegoes
Copy link
Member Author

jdegoes commented Nov 22, 2022

A subproblem here is to merely translate HttpCodec into a fragment of OpenAPI.

This can be done by adding def toOpenAPI: zio.http.api.openapi.OpenAPIBase (or similar) to HttpCodec, and then implementing it on a case-by-case basis for all subtypes, by using the most relevant OpenAPI data types.

Work can be checked by, for example, inspecting the JSON output or converting it to PDF or other form intended for human consumption.

@wpoosanguansit
Copy link
Contributor

wpoosanguansit commented Nov 23, 2022

Hi John, I hope you do not mind providing more detail on the ticket. OpenAPIBase is private so it is not accessible in EndpointSpec. I made it public but EndpointSpec is a case class. I got the error:

class EndpointSpec needs to be abstract. Missing implementation for:
[error] def toOpenApi: zio.http.api.openapi.OpenAPIBase

That would lead to generating the OpenApi as a complete object as:

openapi.OpenAPI.OpenAPI(
    openapi = "",
    info = Info(
      title = "",
      description = Doc.empty,
      termsOfService = URI.create(""),
      contact = None,
      license = None,
      version = "",
    ),
    servers = List.empty[Server],
    paths = Map.empty[Path, PathItem],
    components = None,
    security = List.empty[SecurityRequirement],
    tags = List.empty[Tag],
    externalDocs = None,
  )

In this case, how do I go about extracting the data from EndpointSpec to fill up OpenAPI? It is pretty challenging to extract data from the generics in EndpointSpec[Input, Output].

On the HttpCodec side, I am looking at the most obvious to get started and I do see:

private[api] final case class Body[A](schema: Schema[A]) extends Atom[CodecType.Body, A]

which could be required to return OpenApi's

final case class RequestBody(description: Doc, content: Map[String, MediaType], required: Boolean = false)
extends ResponseOrReference

Can you point me to where I can find out how I can pull out Doc, Map[String, MediaType] and required flag from schema passed in? Thank you for your help.

@987Nabil
Copy link
Contributor

@wpoosanguansit the OpenAPI data structures represent the full open api spec. And it is very likely that currently not all fields can be filled from the data present in EndpointSpec. So it might be, that doc is just Doc.empty.
If you like, we can maybe pair on this. I'll write to you in discord.

@987Nabil
Copy link
Contributor

987Nabil commented Nov 26, 2022

@jdegoes
@wpoosanguansit and I noticed some edges.

  1. OpenApi references documentation of path parameters by the name of its placeholder. But zio http does not offer to explicitly name path params. One could generate names, but I guess this is not desirable by the user that generates API docs. I think we need some API to have named text codecs for path. So instead of EndpointSpec.get("users" / int).out[Int] maybe something like EndpointSpec.get("users" / int("users-per-page")).out[Int]. This is btw no problem to for query params and headers since they are named by default. Or the body since there is only one. So I think this is only a problem of Route. Maybe it just needs a name parameter.
  2. Right now, we can add docs to every HttpCodec. So we can write something like EndpointSpec.get((("users" / uuid) ?? Doc.p("Customer UUID")) / int), but does this really makes sense? Why do you combine a literal and a placeholder/var and document this? And when you have two vars EndpointSpec.get(((int / uuid) ?? Doc.p("Whatever"))) and generate docs like open api, do you give the doc to the int or uuid param? Or both? I think we should allow docs only for single placeholders/vars or the entire EndpointSpec. So you can document the Atoms and the whole, but not any arbitrary combination of Atoms.

What do you think?

@fancellu
Copy link

FYI, I created some models for openAI
https://github.com/fancellu/openai-scala-models
Have used them from ZIO and Http4s (Look in the README)

@987Nabil
Copy link
Contributor

@fancellu We have already a model for OpenAPI in zio-http at zio.http.api.openapi.OpenAPI. And if you actually suggest to use your lib, we want to keep the dependencies of zio-http as few as possible.

Also I think what needs improvement here is more likely the zio-http internal representation of endpoints. The OpenAPI model should be in an okay(ish) state.

@jdegoes
Copy link
Member Author

jdegoes commented Apr 18, 2023

/bounty $500

@algora-pbc
Copy link

algora-pbc bot commented Apr 18, 2023

💎 $500 bounty created by jdegoes
🙋 If you start working on this, comment /attempt #1498 to notify everyone
👉 To claim this bounty, submit a pull request that includes the text /claim #1498 somewhere in its body
📝 Before proceeding, please make sure you can receive payouts in your country
💵 Payment arrives in your account 2-5 days after the bounty is rewarded
💯 You keep 100% of the bounty award
🙏 Thank you for contributing to zio/zio-http!

Attempt Started (GMT+0) Solution
🔴 @kitlangton Jul 29, 2023, 5:37:55 PM WIP
🟢 @987Nabil #2470

@987Nabil
Copy link
Contributor

I am on it 🙂

@pjfanning
Copy link

@987Nabil I don't know if this helps but I maintain https://github.com/swagger-akka-http/swagger-scala-module

This lib supports OpenAPI3 and basically generates Open API models for Scala classes. I have some related libs for non-core Scala classes.

Even if this lib is not useful to you, I would be interested in looking at any alternative solution you use for generating Open API models - as I might be able to learn something that could be useful in swagger-scala-module.

@987Nabil
Copy link
Contributor

Hey @pjfanning! Thanks for the hint. But I think this are two different approaches to the problem. We are actually not analysing the Scala data structures itself, but only a Schema of them based on the zio-schema lib. So we have only a reified version of the data structures. Therefore, the first version I am writing now, focus on the information that are easily accessible by these schemas and the endpoint type it self. And I will most likely make some assumptions and decisions for default behaviour in some cases, that a user might want to configure. But I don't expect that any kind of config will be available in the first impl.
Our goal is as well, to have Endpoint definitions that can not only generate OpenAPI, but also other documentation formats. Therefore I doubt, that zio-http will ever have OpenAPI specific annotations. Maybe we will endup having a config object that you can handover to the generator function. But I am not sure yet about it.

@pjfanning
Copy link

@987Nabil Sounds like you have a good idea of how to proceed. It would be nice to be able to provide a partial definition of the OpenAPI doc when requesting the full doc. The EndpointSpec can provide details to generate server details, API details, model details, etc. There is a fair amount of metadata you should include in an OpenAPI doc though - for instance, the 'Info' object in https://swagger.io/specification/.

@987Nabil
Copy link
Contributor

987Nabil commented Apr 22, 2023

@pjfanning We have already a domain model for the OpenAPI spec in zio-http. Including Info. I expect that you will be able to modify this object manually, if you want to, before you will hand this over to something that will generate for you the actual json/html that you might want to offer via an endpoint.

@fernanluyano
Copy link

Any updates on this?

@algora-pbc
Copy link

algora-pbc bot commented Aug 5, 2023

@kitlangton: Reminder that in 7 days the bounty will become up for grabs, so please submit a pull request before then 🙏

@algora-pbc
Copy link

algora-pbc bot commented Oct 5, 2023

💡 @987Nabil submitted a pull request that claims the bounty. You can visit your org dashboard to reward.

jdegoes pushed a commit that referenced this issue Nov 29, 2023
…2470)

* Improve OpenAPI model; Add OpenAPI generator for EndpointAPI (#1498)

* Minimize schema for optional fields

* Integrate main changes

* Fix Scala 3 build

* Fix exhaustive matching

* More OpenAPI generation tests

* Use latest zio-schema snapshot for Scala 3 macro derivation fix

* Formatting

* OpenAPI tests now compare json ASTs, to avoid string render differences

* Refactoring

* improve docs (#2482)

* Add a test of a middleware providing a context to a `Routes` (#2487)

* Add a test of a middleware providing a context to a `Routes`

* Add a test of a middleware providing a context to a `Routes`

* scalafmt

* scalafmt

* Remove usage of deprecated method in build.sbt (#2486)

* Update sbt-github-actions to 0.18.0 (#2484)

* Update sbt-github-actions to 0.18.0

* Regenerate GitHub Actions workflow

Executed command: sbt githubWorkflowGenerate

* Update netty-codec-http, ... to 4.1.100.Final (#2485)

* Generate readme

* OpenAPI gen support for all kinds of enums with(out) discriminators

OpenAPI gen support for default values, optional and transient fields

---------

Co-authored-by: TomTriple <mail@tomhoefer.de>
Co-authored-by: Jules Ivanic <jules.ivanic@gmail.com>
Co-authored-by: Scala Steward <43047562+scala-steward@users.noreply.github.com>
Copy link

algora-pbc bot commented Nov 29, 2023

🎉🎈 @987Nabil has been awarded $500! 🎈🎊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💎 Bounty enhancement New feature or request 💰 Rewarded ziohackathon An official ticket for ZIO Hackathon
Projects
Development

Successfully merging a pull request may close this issue.

7 participants