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

Swagger Integration #310

Closed
dustinmoris opened this issue Sep 30, 2018 · 6 comments
Closed

Swagger Integration #310

dustinmoris opened this issue Sep 30, 2018 · 6 comments

Comments

@dustinmoris
Copy link
Member

dustinmoris commented Sep 30, 2018

Hi,

First I'd like to say sorry for being really slow with getting the Swagger integration working and letting many people down.

Secondly I want to say thanks to all the people who have forked Giraffe and created an unofficial solution or NuGet package to step in where I was not able to provide a working version yet. Also much appreciated for the PR which was sitting around for a while and then has been closed out of frustration which I understand.

Thirdly I want to say that the Swagger integration has been on my mind for a while now and I am 100% committed in getting something working soon, but I need some help.

I was looking at various solutions which have been going around and to be honest while some of them were very good, there was not a single one where I thought this is it. I want to create something which is at least as smooth and easy to integrate as ASP.NET MVC with Swashbuckle. Personally I don't see a reason why an F# web framework should have anything less fancy than what the C# folks have and with that in mind I don't want to create another DSL or have some unidiomatic attributes.

Ideally a Swagger integration with Giraffe should be as easy as adding another NuGet package and maybe a couple lines of code in the Startup class. This is the end goal and there's maybe some ways to achieve this.

Idea

Currently I am playing with the idea to slightly modify or extend the current function signature which gets passed into the GiraffeMiddleware.

A giraffe web application can easily be viewed as a big decision tree with the middleware (or the top most handler) being the root of the tree and each leaf representing a final endpoint. Traversing the tree down to each leaf basically describes everything about each individual endpoint what the application has defined (the HTTP verb, the route, what content gets returned, etc.).

My idea would be to create a new object, let's call it ApiTree for now, which gets passed into the top most handler and gets passed down all the way to the last handler, with each handler adding to the current node of the ApiTree what the handler is responsible for. This could be done entirely separately at startup without affecting a Giraffe application during runtime later.

At the end the GiraffeMiddleware can retrieve the ApiTree and HttpHandler objects separately and plug the HttpHandler into ASP.NET's pipeline like it already does today and then use the ApiTree to perform further transformations to create a swagger document.

Abstracting the Giraffe web application into an ApiTree would be step 1. This ApiTree object can look whatever we think makes the most sense. This should be a Giraffe internal object which later can be used for other useful things (got some ideas here).

Step 2 would be to flatt the ApiTree into some sort of API description. Basically make each leaf (and all the information with it) an endpoint in a list of endpoints. For this step I would like us to look at possible existing formats. Perhaps this is something where we can re-use an existing .NET API description (ApiExplorer?) which then can already be consumed by another library which can generate a Swagger or RAML document from it.

Step 3 would be generating the actual Swagger/RAML document. This should definitely be done by a third party (ideally). I would highly recommend to look at existing NuGet libraries for that.

Architecting the transformation from a Giraffe web app into a Swagger document like this allows us to divide the problem into smaller sub tasks and hopefully create an API which from an end user's point of view requires almost zero setup to get a Swagger integration in the future.

I think the largest chunk of work will be to generate that initial ApiTree.

This is just an idea and I'd like to hear if someone has a better idea or already tried or done something similar like this with Giraffe.

Anyways, long story short, this is all I have so far and I'd appreciate any help on this subject. I'm not going to have enough time to work on this every day now, but I'll start a new branch soon and commit some code which can be reviewed and contributed to by anyone who wants to help.

Thank you and hope we can make this a reality soon!

@NatElkins
Copy link

Don't be so hard on yourself! It's a team effort.

Wouldn't traversing the ApiTree be a very big breaking change, essentially changing the signature of all handlers? Also, how do you traverse the tree? I have a bunch of code that is similar to this:

            match Helper.validateContext ctx with
            | Error statusCode -> setStatusCode statusCode
            | Ok userId ->
                match getSearchParams ctx with
                | Error errMsg -> (setStatusCode StatusCodes.Status400BadRequest >=> setBodyFromString errMsg)
                | Ok (query, offset, useAws, _) -> getResult userId query offset useAws 

In your vision, how do I get information about those responses into the ApiTree object without calling the routes?

@NatElkins
Copy link

@dustinmoris Hi, just wanted to check in. Can show some psuedo-code/function signatures that explain your idea a bit more? I have some time here and there that I can put towards this feature.

@dustinmoris
Copy link
Member Author

dustinmoris commented Nov 26, 2018

So update:

  • In .NET Core 2.2 there will be a new endpoint routing API, which essentially allows ASP.NET Core middleware (such as MVC) register routes in a common fashion, which will make route information available to all other middleware too. Initially, in 2.2, this will apparently only be there for MVC.
  • In .NET Core 3.0 this feature will get extended so that it will work great outside of MVC too. This new API will then allow other libraries, such as NSwag and Swashbuckle to consume the metadata for all routes without knowing if they came from MVC/Giraffe/whatever.

With that information it makes the most sense to keep an eye on the development of the endpoint routing API and make sure that Giraffe will be able to nicely register it's routes with that new API. If that will work then we might get the Swagger integration for free in the future.

Meanwhile I took the time to digest the (old) PR by @rflechner who built a reflection based API (which I really like) to analyse and compose an API description from which a Swagger.json document gets generated. I've ported his work from the PR into a new repo called Graffe.Swagger.

The code has been committed to the develop branch and builds fine and the sample app was working for me.

Next steps:

  • Get the library up to speed with latest Giraffe changes (addition of ShortGuids for example)
  • Add a lot of info and documentation into the README of the project (similar to what other Giraffe libraries have)
  • Configure AppVeyor to build and create NuGet packages
  • Release first alpha version

@NatElkins Help is very much appreciated! Now that there is code in GitHub feel free to send any PRs as you see fit!

@NatElkins
Copy link

@dustinmoris Do you know where I can find docs on this new routing API?

@dustinmoris
Copy link
Member Author

@NatElkins

Most of the info I've obtained from an ASP.NET Core community standup and Steve Gordon's blog post and an official ASP.NET blog post.

@dustinmoris
Copy link
Member Author

The new OpenAPI integration with the new endpoint routing API might still be a bit further away (since it's not usable yet) so I've migrated the previous Giraffe.Swagger PR into its own repo and cleaned everything up a bit and made sure that it builds and works. I yet have to give it a good test and set up the deployment pipeline to produce a NuGet package but it's close to release. I've also invited @rflechner as a contributor to Giraffe and hope he'll accept my invitation :) (he's done some amazing work there).

https://github.com/giraffe-fsharp/Giraffe.Swagger

I'm moving this thread to: giraffe-fsharp/Giraffe.Swagger#1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants