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

Add default endpoint descriptions #60

Open
fizruk opened this issue Dec 20, 2016 · 5 comments
Open

Add default endpoint descriptions #60

fizruk opened this issue Dec 20, 2016 · 5 comments

Comments

@fizruk
Copy link
Member

fizruk commented Dec 20, 2016

Writing endpoint descriptions is important, but sometimes we don't do this.
This is fine for default Swagger UI, but some more fancy versions (like https://github.com/jensoleg/swagger-ui) don't display the list of endpoints nicely when there are no descriptions:
screen shot 2016-12-20 at 23 21 40

So maybe we should provide some mechanism to generate at least some sort of descriptions
(semi-)automatically.

@phadej
Copy link
Contributor

phadej commented Dec 21, 2016

I remember there was discussion about Description "foo" :> subapi swagger combinator. Was it rejected since?

Though default endpoint would be still cool

@saevarb
Copy link

saevarb commented Mar 27, 2017

Hi. I think this library is enormously awesome, except for the fact that it doesn't automatically generate any endpoint descriptions or operation IDs for the endpoints.

The swagger specification says that operation IDs may be used by clients to uniquely identify endpoints. This causes libraries like martian to not return any endpoints, because it uses the operation IDs to construct the structure which contains the endpoints(An operation with ID "fooBar" lets me call that endpoint using a keyword :foo-bar).

I would definitely be willing to look into implementing this, but I'm not familiar with the type-level magic that is going on, and I'm not sure I'm willing to spend too much time on it if it's too complicated. Do you guys have a recommended starting point for adding such combinators? Also, if I were to implement support for, say, a Desc and an OperationId combinators, what would the preferred usage scenario look like? Just type Foo = MyEndpoint :> Desc "Endpoint" :> OpId "getFoo"? Would it require a new type operator? And I gather that these combinators would not be compatible with servant as-is, and would need to be removed by some type-level machinery before passing them on?

@fizruk
Copy link
Member Author

fizruk commented May 29, 2017

Disclaimer: this code is untested.

To add a description combinator you can do this:

-- | Add a description for a (part of) API.
data Description (sym :: Symbol)

-- | A handy infix version of 'Description'.
type (--|) api sym = Description sym :> api

type MyAPI
    = GetResource    --| "Get resource by ID."
 :<|> DeleteResource --| "Delete resource by ID."

type GetResource    = QueryParam "resource_id" ResourceId :> Get    '[JSON] Resource
type DeleteResource = QueryParam "resource_id" ResourceId :> Delete '[JSON] Resource

-- or

type MyAPI = GetResource :<|> DeleteResource

type GetResource
  = Description "Get resource by ID."
 :> QueryParam "resource_id" ResourceId
 :> Get '[JSON] Resource

type DeleteResource
  = Description "Delete resource by ID."
 :> QueryParam "resource_id" ResourceId
 :> Delete '[JSON] Resource

Instances for Description:

{-# LANGUAGE ScopedTypeVariables #-}

instance HasServer api => HasServer (Description desc :> api) where
  type ServerT (Description desc :> api) m = ServerT api m
  route _ = route (Proxy :: Proxy api)

instance HasClient api => HasClient (Description desc :> api) where
  type Client (Description desc :> api) = Client api
  clientWithRoute _ = clientWithRoute (Proxy :: Proxy api)

instance HasSwagger api => HasSwagger (Description desc :> api) where
  toSwagger _ = toSwagger (Proxy :: Proxy api)
    & allOperations.description %~ (Just (symbolVal (Proxy :: Proxy desc)) <>)

It is possible that you want summary instead of description in HasSwagger instance.

@KtorZ
Copy link

KtorZ commented Oct 1, 2017

Hey!

For those still interested, the following actually works fine for me (thanks @fizruk):

import           Control.Lens         ((&), (?~))
import           GHC.TypeLits         (KnownSymbol, Symbol)
import           Servant              ((:>), HasServer, ServerT)
import           Data.Proxy           (Proxy (..))
import           Data.Swagger         (ToSchema)

import qualified Data.Swagger         as Swagger
import qualified Data.Text            as Text
import qualified GHC.TypeLits         as GHC
import qualified Servant
import qualified Servant.Swagger      as Servant


data Description (desc :: Symbol)


type (--|) api desc =
  Description desc :> api


instance HasServer api ctx => HasServer (Description desc :> api) ctx where
  type ServerT (Description desc :> api) m =
    ServerT api m

  route _ =
    Servant.route (Proxy :: Proxy api)


instance (KnownSymbol desc, HasSwagger api) => HasSwagger (Description desc :> api) where
  toSwagger _ =
    Servant.toSwagger (Proxy :: Proxy api)
      & Swagger.allOperations . Swagger.description ?~ Text.pack (GHC.symbolVal (Proxy :: Proxy desc))

fisx pushed a commit to wireapp/servant-swagger that referenced this issue Jun 1, 2019
@akhesaCaro
Copy link
Contributor

Hi,
Servant-swagger will be moved into the main Servant repo (see : haskell-servant/servant#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!

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

5 participants