pheymann / typedapi

Build your web API on the type level.
MIT License
161 stars 10 forks source link

Add a Swagger support #9

Open mielientiev opened 6 years ago

mielientiev commented 6 years ago

It would be great if it will be possible to generate a swagger json file for the server side API.

pheymann commented 6 years ago

Hi, I think this will need some work and time as we have to:

Maybe if you have some time left you could already start and take a look into that issue and find first solutions for some of the points.

pheymann commented 6 years ago

Started to compile some documentation on how the internals work (far from complete):

https://github.com/pheymann/typedapi/blob/master/docs/UnderTheHood_Client.md

pheymann commented 6 years ago

Hi, I am not sure if you already got the time to take a look at this topic. In case you did, I did some bigger clean-ups in the internal code base. Have a look at #13 or ask me if there is a problem.

wookievx commented 6 years ago

Hi, I have recently picked up the idea of generating documentation. Tried to utilize docless because it is written purely in scala, not dependent on reflection-based swagger java. Unfortunately it is not developed since Mar 18, 2017 and it is depending on really old versions of common libraries like circe and cats, not to mention that it does not support quite a few features of OpenAPI (previously Swagger). I suggest we introduce our own intermediate model for documentation containing all necessary API information, derived from endpoint definition, that can be transformed to potentially many formats, (in the foreseeable future it will probably be only one). Derivation of documentation model should be very similar to server derivation.

pheymann commented 6 years ago

Hi @wookievx , I think it makes no sense to introduce a library which is 1) stale and 2) brings something like cats into scope. So far I managed to keep the core dependency footprint of Typedapi as small as possible by only introducing shapeless.

I also agree that it would make more sense to create some abstract representation of your documentation and to implement different backends/engines to render this representation to Swagger or whatever format.

I think building up some ADT during compilation makes sense:

sealed trait Documentation
final case class Endpoint(method: Method, path: Seq[Path], ...) extends Documentation

sealed trait Path extends Documentation
final case class PathElement(name: String) extends Path
final case class Segment(name: String, tpe: String) extends Path

...

What we can do derive this ADT during compilation as we do with the RequestDataBuilder or RouteExtractor.

What we end up with is an abstract representation - as you said - which we then can render.

I think one of the bigger tasks will be to derive a e.g. Swagger representation for the classes we return as JSON. But using LabelledGeneric here should be straight forward.

Do you have time to look into this a bit more and to create some initial WIP PR or even final PR for this task?

wookievx commented 5 years ago

Recently i picked up some resolve to tackle this issue after discovering that this library provide almost full coverage of OpenApi specification. Unfortunately it is not separate from the of the "rest-framework" which is redundant from typeadapi perspective and brings a few binaries with it (unfortunately it cannot be reduced even by extracting open-api specific part away). So i decided to fork your repository and work on documenting API separately. I have currently implemented type-class based model instead of some format agnostic AST, which will require some polishing but seems well aligned with current way of handling routes.

On the other hand current approach to responding with different response codes is not applicable to automatic documentation generation. It would require api to evolve in a way that encodes status codes and respective responses at least to some degree on the type-level.

pheymann commented 5 years ago

Hi @wookievx , thanks for taking a detailed look into the Swagger task and sorry that it took me so long to respond - a lot to do lately.

I will take a closer look soon and will try to help you with the status code issue.

wookievx commented 5 years ago

Hello, Regarding response code encoding, i think it is hard to do it properly. What we can do i think is abstract away Result[A] and leave it for the specific backend to implement (for http4s it could be via MonadError instance when it comes down to errors). Then if result type of specific endpoint is some sort of coproduct (i will use shapeless notation, but it not necessarily has to be the solution):

val MyApi = := :> Get[Json, A :+: B :+: C :+: CNil]

specific backend may decide what status code is associated with the given type and the same approach can be used by documenting module (we might introduce typeclass ResponseCodeOf[A], but there is some relevant discussion whether response codes are important at all in the first place, so i don't think we should be too opinionated about them).