@@ -15,7 +15,12 @@
1515--
1616-- @docs :: 'HasDocs' api => 'Proxy' api -> 'API'@
1717--
18−-- You can then call 'markdown' on it:
18+-- Alternately, if you wish to add one or more introductions to your
19+-- documentation, use 'docsWithIntros':
20+--
21+-- @docsWithIntros :: 'HasDocs' api => [DocIntro] -> 'Proxy' api -> 'API'@
22+--
23+-- You can then call 'markdown' on the 'API' value:
1924--
2025-- @markdown :: 'API' -> String@
2126--
@@ -30,61 +35,92 @@
3035-- markdown pretty printer in action:
3136--
3237-- > {-# LANGUAGE DataKinds #-}
33−-- > {-# LANGUAGE PolyKinds #-}
34−-- > {-# LANGUAGE TypeFamilies #-}
3538-- > {-# LANGUAGE DeriveGeneric #-}
3639-- > {-# LANGUAGE TypeOperators #-}
3740-- > {-# LANGUAGE FlexibleInstances #-}
3841-- > {-# LANGUAGE OverloadedStrings #-}
39−-- >
42+-- > {-# OPTIONS_GHC -fno-warn-orphans #-}
43+-- > import Data.Aeson
4044-- > import Data.Proxy
41−-- > import Data.Text
42−-- > import Servant
45+-- > import Data.Text(Text)
46+-- > import GHC.Generics
47+-- > import Servant.API
48+-- > import Servant.Docs
4349-- >
44−-- > -- our type for a Greeting message
45−-- > data Greet = Greet { _msg :: Text }
50+-- > -- * Example
51+-- >
52+-- > -- | A greet message data type
53+-- > newtype Greet = Greet Text
4654-- > deriving (Generic, Show)
4755-- >
48−-- > -- we get our JSON serialization for free
4956-- > instance FromJSON Greet
5057-- > instance ToJSON Greet
5158-- >
52−-- > -- we provide a sample value for the 'Greet' type
53−-- > instance ToSample Greet where
54−-- > toSample = Just g
59+-- > -- We add some useful annotations to our captures,
60+-- > -- query parameters and request body to make the docs
61+-- > -- really helpful.
62+-- > instance ToCapture (Capture "name" Text) where
63+-- > toCapture _ = DocCapture "name" "name of the person to greet"
5564-- >
56−-- > where g = Greet "Hello, haskeller!"
65+-- > instance ToCapture (Capture "greetid" Text) where
66+-- > toCapture _ = DocCapture "greetid" "identifier of the greet msg to remove"
5767-- >
5868-- > instance ToParam (QueryParam "capital" Bool) where
5969-- > toParam _ =
6070-- > DocQueryParam "capital"
6171-- > ["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."
74+-- > Normal
6375-- >
64−-- > instance ToCapture (Capture "name" Text) where
65−-- > toCapture _ = DocCapture "name" "name of the person to greet"
76+-- > instance ToSample Greet where
77+-- > toSample = Just $ Greet "Hello, haskeller!"
78+-- >
79+-- > toSamples =
80+-- > [ ("If you use ?capital=true", Greet "HELLO, HASKELLER")
81+-- > , ("If you use ?capital=false", Greet "Hello, haskeller")
82+-- > ]
83+-- >
84+-- > intro1 :: DocIntro
85+-- > intro1 = DocIntro "On proper introductions." -- The title
86+-- > [ "Hello there."
87+-- > , "As documentation is usually written for humans, it's often useful \
88+-- > \to introduce concepts with a few words." ] -- Elements are paragraphs
89+-- >
90+-- > intro2 :: DocIntro
91+-- > intro2 = DocIntro "This title is below the last"
92+-- > [ "You'll also note that multiple intros are possible." ]
6693-- >
67−-- > instance ToCapture (Capture "greetid" Text) where
68−-- > toCapture _ = DocCapture "greetid" "identifier of the greet msg to remove"
6994-- >
7095-- > -- API specification
7196-- > type TestApi =
97+-- > -- GET /hello/:name?capital={true, false} returns a Greet as JSON
7298-- > "hello" :> Capture "name" Text :> QueryParam "capital" Bool :> Get Greet
73−-- > :<|> "greet" :> RQBody Greet :> Post Greet
74−-- > :<|> "delete" :> Capture "greetid" Text :> Delete
99+-- >
100+-- > -- POST /greet with a Greet as JSON in the request body,
101+-- > -- returns a Greet as JSON
102+-- > :<|> "greet" :> ReqBody Greet :> Post Greet
103+-- >
104+-- > -- DELETE /greet/:greetid
105+-- > :<|> "greet" :> Capture "greetid" Text :> Delete
75106-- >
76107-- > testApi :: Proxy TestApi
77108-- > testApi = Proxy
78109-- >
79−-- > -- Generate the Documentation's ADT
80−-- > greetDocs :: API
81−-- > greetDocs = docs testApi
82−-- >
110+-- > -- Generate the data that lets us have API docs. This
111+-- > -- is derived from the type as well as from
112+-- > -- the 'ToCapture', 'ToParam' and 'ToSample' instances from above.
113+-- > --
114+-- > -- If you didn't want intros you could just call:
115+-- > --
116+-- > -- > docs testAPI
117+-- > docsGreet :: API
118+-- > docsGreet = docsWithIntros [intro1, intro2] testApi
83119-- > main :: IO ()
84−-- > main = putStrLn $ markdown greetDocs
120+-- > main = putStrLn $ markdown docsGreet
85121module Servant.Docs
86122 ( -- * 'HasDocs' class and key functions
87− HasDocs(..), docs, markdown
123+ HasDocs(..), docs, docsWithIntros, markdown
88124
89125 , -- * Classes you need to implement for your types
90126 ToSample(..)
@@ -92,7 +128,6 @@ module Servant.Docs
92128 , sampleByteStrings
93129 , ToParam(..)
94130 , ToCapture(..)
95− , ToIntro(..)
96131
97132 , -- * ADTs to represent an 'API'
98133 Method(..)
@@ -101,7 +136,7 @@ module Servant.Docs
101136 , DocCapture(..), capSymbol, capDesc
102137 , DocQueryParam(..), ParamKind(..), paramName, paramValues, paramDesc, paramKind
103138 , DocNote(..), noteTitle, noteBody
104− , DocIntro(..), Intro
139+ , DocIntro(..)
105140 , Response, respStatus, respBody, defResponse
106141 , Action, captures, headers, notes, params, rqbody, response, defAction
107142 , single
@@ -204,22 +239,6 @@ instance Monoid API where
204239 API a1 b1 `mappend` API a2 b2 = API (a1 <> a2) (b1 <> b2)
205240 mempty = API mempty mempty
206241
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.
209−--
210−-- Example:
211−--
212−-- > type OurAPI = "users" :> Get [User]
213−-- > type IntroducedAPI = Intro "of human bondage" :> OurAPI
214−-- >
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."
219−-- > ]
220−--
221−data Intro (name :: Symbol)
222−
223242-- | An empty 'API'
224243emptyAPI :: API
225244emptyAPI = mempty
@@ -245,9 +264,8 @@ data DocQueryParam = DocQueryParam
245264 , _paramKind :: ParamKind
246265 } deriving (Eq, Show)
247266
248−-- | An introductory paragraph for your documentation. You can attach these
249−-- with the 'Intro' type.
250−--
267+-- | An introductory paragraph for your documentation. You can pass these to
268+-- 'docsWithIntros'.
251269data DocIntro = DocIntro
252270 { _introTitle :: String -- ^ Appears above the intro blob
253271 , _introBody :: [String] -- ^ Each String is a paragraph.
@@ -356,10 +374,16 @@ makeLenses ''DocNote
356374makeLenses ''Response
357375makeLenses ''Action
358376
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.
360379docs :: HasDocs layout => Proxy layout -> API
361380docs p = docsFor p (defEndpoint, defAction)
362381
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
386+
363387-- | The class that abstracts away the impact of API combinators
364388-- on documentation generation.
365389class HasDocs layout where
@@ -436,14 +460,6 @@ class ToParam t where
436460class ToCapture c where
437461 toCapture :: Proxy c -> DocCapture
438462
439−-- | The class to define the contents of an 'Intro'
440−-- Example of an instance:
441−--
442−-- > instance ToIntro "an intro" where
443−-- > toIntro _ = DocIntro "This is some text"
444−class ToIntro (intro :: Symbol) where
445− toIntro :: Proxy intro -> DocIntro
446−
447463-- | Generate documentation in Markdown format for
448464-- the given 'API'.
449465markdown :: API -> String
@@ -693,15 +709,6 @@ instance (KnownSymbol path, HasDocs sublayout) => HasDocs (path :> sublayout) wh
693709 endpoint' = endpoint & path <>~ [symbolVal pa]
694710 pa = Proxy :: Proxy path
695711
696−instance (KnownSymbol intro, HasDocs sublayout, ToIntro intro)
697− => HasDocs (Intro intro :> sublayout) where
698−
699− docsFor Proxy x =
700− docsFor sublayoutP x & apiIntros %~ (toIntro intro <|)
701− where sublayoutP = Proxy :: Proxy sublayout
702− intro :: Proxy intro
703− intro = Proxy
704−
705712{-
706713
707714-- | Serve your API's docs as markdown embedded in an html \<pre> tag.