servant-swagger icon indicating copy to clipboard operation
servant-swagger copied to clipboard

Helpers for serving swagger.json on server

Open fizruk opened this issue 9 years ago • 10 comments

This is what I've been using:

type WithSwagger api = api :<|> ("swagger.json" :> Get '[JSON] Swagger)

serveWithSwagger :: forall layout. HasServer layout => Swagger -> Proxy layout -> Server layout -> Application
serveWithSwagger spec _ serv = serve (Proxy :: Proxy (WithSwagger layout)) (serv :<|> return spec)

This can be probably generalized so that we won't have to depend on servant-server:

withSwagger :: forall m api application server. Monad m
  => (Proxy (WithSwagger api) -> (server :<|> m Swagger) -> application)  -- serve
  -> Swagger -> Proxy api -> server -> application
withSwagger serve spec _ server =
  serve (Proxy :: Proxy (WithSwagger layout)) (server :<|> return spec)

-- usage example
withSwagger serve (toSwagger api) api server

fizruk avatar Jan 21 '16 22:01 fizruk

Since some APIs might use Raw, we probably should prepend swagger endpoint.

fizruk avatar Jan 21 '16 22:01 fizruk

Would it be possible for toSwagger to ignore a Swagger in the API, making the above unnecessary ?

simonmichael avatar Jan 22 '16 18:01 simonmichael

Would it be possible for toSwagger to ignore a Swagger in the API, making the above unnecessary ?

That sounds like a good idea. The nice thing is that we can actually document the existence of the Swagger endpoint too.

Prior to Verb, this requires 10 new overlapping instances. With Verb, it'll be two.

jkarni avatar Jan 22 '16 18:01 jkarni

The nice thing is that we can actually document the existence of the Swagger endpoint too.

Do we want instance ToSchema Swagger? And do we want that schema definition polluting the list of definitions? Not sure.

I don't mind adding 10 overlapping instances to ignore endpoints ending in Swagger.

fizruk avatar Jan 23 '16 01:01 fizruk

Problem

I think this issue actually gives rise to at least these scenarios:

  1. We don't want Swagger endpoint(s) to appear in swagger.json.
  2. We want Swagger endpoint(s) to appear, but don't want to pollute definitions.
  3. We want Swagger endpoint(s) to appear with an accurate schema.

Solutions

Possible solutions to 1:

  1. Introduce WithSwagger helpers as show above.
  2. Always ignore Swagger enpoints (with specific instances of HasSwagger).
  3. Provide a helper type family RemoveEndpoint endpoint api.
  4. Provide a function removeEndpoint and use it to build toSwagger_ which removes Swagger endpoints at term-level.
  5. Provide Ignored a newtype to indicate directly in the API that a type should be ignored by servant-swagger.

Possible solutions to 2:

  1. Provide an incomplete ToSchema instance for Swagger (with externalDocs pointing to the online swagger specification).
  2. Provide a function truncateSchema to truncate any schema to its type and description/docs, use that to build toSwaggerTruncate which would truncate schema for Swagger.
  3. Provide a newtype Truncated a to mark type's schema as truncated in the API.

Possible solutions to 3:

  1. Just provide a complete ToSchema instance for Swagger.

Conclusion

I assume that we want to support all of the scenarios.

I am inclined to this:

  • provide a complete ToSchema instance for Swagger;
  • provide a function truncateSchema to truncate any schema to its type and description/docs, use that to build toSwaggerTruncate which would truncate schema for Swagger;
  • provide a function removeEndpoint and use it to build toSwagger_ which removes Swagger endpoints at term-level.

@jkarni @dmjio what do you think?

Questions to think about:

  • are there any other possible use cases we'd like to support?
  • are there other solutions?
  • what should be the default recommended case?

fizruk avatar Feb 04 '16 00:02 fizruk

I came up with something similar. Some observations:

  • I quickly needed to tweak the Swagger manually, so my function takes a Swagger -> Swagger argument.
  • I think the host and basePath should be set automatically by serveWithSwagger.

soenkehahn avatar Mar 03 '16 08:03 soenkehahn

@soenkehahn Swagger -> Swagger makes sense, yes.

I am not sure though how you expect host and basePath to be filled automatically. Are you suggesting to take those from the request?

fizruk avatar Mar 03 '16 08:03 fizruk

Yes, as a small Middleware. As long as servant doesn't offer another way of obtaining this information.

soenkehahn avatar Mar 03 '16 09:03 soenkehahn

There is servant-swagger-ui, which kind of solves this problem (with a little extra stuff though)?

phadej avatar Feb 01 '17 16:02 phadej

Hi, Servant-swagger will be moved into the main Servant repo (see : https://github.com/haskell-servant/servant/pull/1475) If this issue is still relevant, would it be possible for you to summit it there? : https://github.com/haskell-servant/servant/issues

Thanks in advance!

akhesaCaro avatar Nov 17 '21 11:11 akhesaCaro