@@ -15,7 +15,12 @@
16-- @docs :: 'HasDocs' api => 'Proxy' api -> 'API'@
16-- @docs :: 'HasDocs' api => 'Proxy' api -> 'API'@
18+-- Alternately, if you wish to add one or more introductions to your
19+-- documentation, use 'docsWithIntros':
21+-- @docsWithIntros :: 'HasDocs' api => [DocIntro] -> 'Proxy' api -> 'API'@
18−-- You can then call 'markdown' on it:
23+-- You can then call 'markdown' on the 'API' value:
20-- @markdown :: 'API' -> String@
25-- @markdown :: 'API' -> String@
@@ -30,61 +35,92 @@
30-- markdown pretty printer in action:
35-- markdown pretty printer in action:
32-- > {-# LANGUAGE DataKinds #-}
37-- > {-# LANGUAGE DataKinds #-}
33−-- > {-# LANGUAGE PolyKinds #-}
34−-- > {-# LANGUAGE TypeFamilies #-}
35-- > {-# LANGUAGE DeriveGeneric #-}
38-- > {-# LANGUAGE DeriveGeneric #-}
36-- > {-# LANGUAGE TypeOperators #-}
39-- > {-# LANGUAGE TypeOperators #-}
37-- > {-# LANGUAGE FlexibleInstances #-}
40-- > {-# LANGUAGE FlexibleInstances #-}
38-- > {-# LANGUAGE OverloadedStrings #-}
41-- > {-# LANGUAGE OverloadedStrings #-}
39−-- >
42+-- > {-# OPTIONS_GHC -fno-warn-orphans #-}
43+-- > import Data.Aeson
40-- > import Data.Proxy
44-- > import Data.Proxy
41−-- > import Data.Text
45+-- > import Data.Text(Text)
46+-- > import GHC.Generics
42−-- > import Servant
47+-- > import Servant.API
48+-- > import Servant.Docs
44−-- > -- our type for a Greeting message
50+-- > -- * Example
52+-- > -- | A greet message data type
45−-- > data Greet = Greet { _msg :: Text }
53+-- > newtype Greet = Greet Text
46-- > deriving (Generic, Show)
54-- > deriving (Generic, Show)
48−-- > -- we get our JSON serialization for free
49-- > instance FromJSON Greet
56-- > instance FromJSON Greet
50-- > instance ToJSON Greet
57-- > instance ToJSON Greet
52−-- > -- we provide a sample value for the 'Greet' type
59+-- > -- We add some useful annotations to our captures,
60+-- > -- query parameters and request body to make the docs
61+-- > -- really helpful.
53−-- > instance ToSample Greet where
62+-- > instance ToCapture (Capture "name" Text) where
54−-- > toSample = Just g
63+-- > toCapture _ = DocCapture "name" "name of the person to greet"
56−-- > where g = Greet "Hello, haskeller!"
65+-- > instance ToCapture (Capture "greetid" Text) where
66+-- > toCapture _ = DocCapture "greetid" "identifier of the greet msg to remove"
58-- > instance ToParam (QueryParam "capital" Bool) where
68-- > instance ToParam (QueryParam "capital" Bool) where
59-- > toParam _ =
69-- > toParam _ =
60-- > DocQueryParam "capital"
70-- > DocQueryParam "capital"
61-- > ["true", "false"]
71-- > ["true", "false"]
62−-- > "Get the greeting message in uppercase (true) or not (false). Default is false."
72+-- > "Get the greeting message in uppercase (true) or not (false).\
73+-- > \Default is false."
64−-- > instance ToCapture (Capture "name" Text) where
76+-- > instance ToSample Greet where
65−-- > toCapture _ = DocCapture "name" "name of the person to greet"
77+-- > toSample = Just $ Greet "Hello, haskeller!"
80+-- > [ ("If you use ?capital=true", Greet "HELLO, HASKELLER")
81+-- > , ("If you use ?capital=false", Greet "Hello, haskeller")
84+-- > intro1 :: DocIntro
85+-- > intro1 = DocIntro "On proper introductions." -- The title
87+-- > , "As documentation is usually written for humans, it's often useful \
88+-- > \to introduce concepts with a few words." ] -- Elements are paragraphs
90+-- > intro2 :: DocIntro
91+-- > intro2 = DocIntro "This title is below the last"
92+-- > [ "You'll also note that multiple intros are possible." ]
67−-- > instance ToCapture (Capture "greetid" Text) where
68−-- > toCapture _ = DocCapture "greetid" "identifier of the greet msg to remove"
70-- > -- API specification
95-- > -- API specification
71-- > type TestApi =
96-- > type TestApi =
97+-- > -- GET /hello/:name?capital={true, false} returns a Greet as JSON
72-- > "hello" :> Capture "name" Text :> QueryParam "capital" Bool :> Get Greet
98-- > "hello" :> Capture "name" Text :> QueryParam "capital" Bool :> Get Greet
100+-- > -- POST /greet with a Greet as JSON in the request body,
101+-- > -- returns a Greet as JSON
73−-- > :<|> "greet" :> RQBody Greet :> Post Greet
102+-- > :<|> "greet" :> ReqBody Greet :> Post Greet
104+-- > -- DELETE /greet/:greetid
74−-- > :<|> "delete" :> Capture "greetid" Text :> Delete
105+-- > :<|> "greet" :> Capture "greetid" Text :> Delete
76-- > testApi :: Proxy TestApi
107-- > testApi :: Proxy TestApi
77-- > testApi = Proxy
108-- > testApi = Proxy
79−-- > -- Generate the Documentation's ADT
110+-- > -- Generate the data that lets us have API docs. This
80−-- > greetDocs :: API
111+-- > -- is derived from the type as well as from
81−-- > greetDocs = docs testApi
112+-- > -- the 'ToCapture', 'ToParam' and 'ToSample' instances from above.
114+-- > -- If you didn't want intros you could just call:
116+-- > -- > docs testAPI
117+-- > docsGreet :: API
118+-- > docsGreet = docsWithIntros [intro1, intro2] testApi
83-- > main :: IO ()
119-- > main :: IO ()
84−-- > main = putStrLn $ markdown greetDocs
120+-- > main = putStrLn $ markdown docsGreet
85module Servant.Docs
121module Servant.Docs
86 ( -- * 'HasDocs' class and key functions
122 ( -- * 'HasDocs' class and key functions
87− HasDocs(..), docs, markdown
123+ HasDocs(..), docs, docsWithIntros, markdown
89 , -- * Classes you need to implement for your types
125 , -- * Classes you need to implement for your types
90 ToSample(..)
126 ToSample(..)
@@ -92,7 +128,6 @@ module Servant.Docs
92 , sampleByteStrings
128 , sampleByteStrings
93 , ToParam(..)
129 , ToParam(..)
94 , ToCapture(..)
130 , ToCapture(..)
97 , -- * ADTs to represent an 'API'
132 , -- * ADTs to represent an 'API'
98 Method(..)
133 Method(..)
@@ -101,7 +136,7 @@ module Servant.Docs
101 , DocCapture(..), capSymbol, capDesc
136 , DocCapture(..), capSymbol, capDesc
102 , DocQueryParam(..), ParamKind(..), paramName, paramValues, paramDesc, paramKind
137 , DocQueryParam(..), ParamKind(..), paramName, paramValues, paramDesc, paramKind
103 , DocNote(..), noteTitle, noteBody
138 , DocNote(..), noteTitle, noteBody
104− , DocIntro(..), Intro
139+ , DocIntro(..)
105 , Response, respStatus, respBody, defResponse
140 , Response, respStatus, respBody, defResponse
106 , Action, captures, headers, notes, params, rqbody, response, defAction
141 , Action, captures, headers, notes, params, rqbody, response, defAction
@@ -204,22 +239,6 @@ instance Monoid API where
204 API a1 b1 `mappend` API a2 b2 = API (a1 <> a2) (b1 <> b2)
239 API a1 b1 `mappend` API a2 b2 = API (a1 <> a2) (b1 <> b2)
205 mempty = API mempty mempty
240 mempty = API mempty mempty
207−-- | A way for a developer to insert an introductory paragraph manually. This
208−-- is not to be used in server or client facing API types.
212−-- > type OurAPI = "users" :> Get [User]
213−-- > type IntroducedAPI = Intro "of human bondage" :> OurAPI
215−-- > instance ToIntro "of human bondage" where
216−-- > toIntro = DocIntro "A title for the intro section"
217−-- > [ "A blob of text that will be at the top."
218−-- > , "List elements are paragraphs."
221−data Intro (name :: Symbol)
223-- | An empty 'API'
242-- | An empty 'API'
224emptyAPI :: API
243emptyAPI :: API
225emptyAPI = mempty
244emptyAPI = mempty
@@ -245,9 +264,8 @@ data DocQueryParam = DocQueryParam
245 , _paramKind :: ParamKind
264 , _paramKind :: ParamKind
246 } deriving (Eq, Show)
265 } deriving (Eq, Show)
248−-- | An introductory paragraph for your documentation. You can attach these
267+-- | An introductory paragraph for your documentation. You can pass these to
249−-- with the 'Intro' type.
268+-- 'docsWithIntros'.
251data DocIntro = DocIntro
269data DocIntro = DocIntro
252 { _introTitle :: String -- ^ Appears above the intro blob
270 { _introTitle :: String -- ^ Appears above the intro blob
253 , _introBody :: [String] -- ^ Each String is a paragraph.
271 , _introBody :: [String] -- ^ Each String is a paragraph.
@@ -356,10 +374,16 @@ makeLenses ''DocNote
356makeLenses ''Response
374makeLenses ''Response
357makeLenses ''Action
375makeLenses ''Action
359−-- | Generate the docs for a given API that implements 'HasDocs'.
377+-- | Generate the docs for a given API that implements 'HasDocs'. This is the
378+-- default way to create documentation.
360docs :: HasDocs layout => Proxy layout -> API
379docs :: HasDocs layout => Proxy layout -> API
361docs p = docsFor p (defEndpoint, defAction)
380docs p = docsFor p (defEndpoint, defAction)
382+-- | Generate the docs for a given API that implements 'HasDocs' with with any
383+-- number of introduction(s)
384+docsWithIntros :: HasDocs layout => [DocIntro] -> Proxy layout -> API
385+docsWithIntros intros p = docs p & apiIntros <>~ intros
363-- | The class that abstracts away the impact of API combinators
387-- | The class that abstracts away the impact of API combinators
364-- on documentation generation.
388-- on documentation generation.
365class HasDocs layout where
389class HasDocs layout where
@@ -436,14 +460,6 @@ class ToParam t where
436class ToCapture c where
460class ToCapture c where
437 toCapture :: Proxy c -> DocCapture
461 toCapture :: Proxy c -> DocCapture
439−-- | The class to define the contents of an 'Intro'
440−-- Example of an instance:
442−-- > instance ToIntro "an intro" where
443−-- > toIntro _ = DocIntro "This is some text"
444−class ToIntro (intro :: Symbol) where
445− toIntro :: Proxy intro -> DocIntro
447-- | Generate documentation in Markdown format for
463-- | Generate documentation in Markdown format for
448-- the given 'API'.
464-- the given 'API'.
449markdown :: API -> String
465markdown :: API -> String
@@ -693,15 +709,6 @@ instance (KnownSymbol path, HasDocs sublayout) => HasDocs (path :> sublayout) wh
693 endpoint' = endpoint & path <>~ [symbolVal pa]
709 endpoint' = endpoint & path <>~ [symbolVal pa]
694 pa = Proxy :: Proxy path
710 pa = Proxy :: Proxy path
696−instance (KnownSymbol intro, HasDocs sublayout, ToIntro intro)
697− => HasDocs (Intro intro :> sublayout) where
700− docsFor sublayoutP x & apiIntros %~ (toIntro intro <|)
701− where sublayoutP = Proxy :: Proxy sublayout
702− intro :: Proxy intro
707-- | Serve your API's docs as markdown embedded in an html \<pre> tag.
714-- | Serve your API's docs as markdown embedded in an html \<pre> tag.