@@ -25,8 +25,8 @@ import Control.Applicative
25import Control.Arrow
25import Control.Arrow
27import Control.Lens
27import Control.Lens
28− (makeLenses, mapped, over, traversed, view, (%~), (&), (.~),
28+ (makeLenses, mapped, over, set, traversed, view, (%~), (&),
29− (<>~), (^.), (|>))
29+ (.~), (<>~), (^.), (|>))
30import qualified Data.ByteString.Char8 as BSC
30import qualified Data.ByteString.Char8 as BSC
31import Data.ByteString.Lazy.Char8
31import Data.ByteString.Lazy.Char8
32 (ByteString)
32 (ByteString)
@@ -64,7 +64,7 @@ import Servant.API
64import Servant.API.ContentTypes
64import Servant.API.ContentTypes
65import Servant.API.TypeLevel
65import Servant.API.TypeLevel
67−import qualified Data.Universe.Helpers as U
67+import qualified Data.Universe.Helpers as U
69import qualified Data.HashMap.Strict as HM
69import qualified Data.HashMap.Strict as HM
70import qualified Data.Text as T
70import qualified Data.Text as T
@@ -161,6 +161,20 @@ data DocQueryParam = DocQueryParam
161 , _paramKind :: ParamKind
161 , _paramKind :: ParamKind
162 } deriving (Eq, Ord, Show)
162 } deriving (Eq, Ord, Show)
164+-- | A type to represent fragment. Holds the name of the fragment and its description.
166+-- Write a 'ToFragment' instance for your fragment types.
167+data DocFragment = DocFragment
168+ { _fragSymbol :: String -- type supplied
169+ , _fragDesc :: String -- user supplied
170+ } deriving (Eq, Ord, Show)
172+-- | There should be at most one 'Fragment' per API endpoint.
173+-- So here we are keeping the first occurrence.
174+combineFragment :: Maybe DocFragment -> Maybe DocFragment -> Maybe DocFragment
175+Nothing `combineFragment` mdocFragment = mdocFragment
176+Just docFragment `combineFragment` _ = Just docFragment
164-- | An introductory paragraph for your documentation. You can pass these to
178-- | An introductory paragraph for your documentation. You can pass these to
165-- 'docsWithIntros'.
179-- 'docsWithIntros'.
166data DocIntro = DocIntro
180data DocIntro = DocIntro
@@ -283,6 +297,7 @@ data Action = Action
283 , _captures :: [DocCapture] -- type collected + user supplied info
297 , _captures :: [DocCapture] -- type collected + user supplied info
284 , _headers :: [Text] -- type collected
298 , _headers :: [Text] -- type collected
285 , _params :: [DocQueryParam] -- type collected + user supplied info
299 , _params :: [DocQueryParam] -- type collected + user supplied info
300+ , _fragment :: Maybe DocFragment -- type collected + user supplied info
286 , _notes :: [DocNote] -- user supplied
301 , _notes :: [DocNote] -- user supplied
287 , _mxParams :: [(String, [DocQueryParam])] -- type collected + user supplied info
302 , _mxParams :: [(String, [DocQueryParam])] -- type collected + user supplied info
288 , _rqtypes :: [M.MediaType] -- type collected
303 , _rqtypes :: [M.MediaType] -- type collected
@@ -296,8 +311,9 @@ data Action = Action
296-- As such, we invent a non-commutative, left associative operation
311-- As such, we invent a non-commutative, left associative operation
297-- 'combineAction' to mush two together taking the response from the very left.
312-- 'combineAction' to mush two together taking the response from the very left.
298combineAction :: Action -> Action -> Action
313combineAction :: Action -> Action -> Action
314+Action a c h p f n m ts body resp
299−Action a c h p n m ts body resp `combineAction` Action a' c' h' p' n' m' ts' body' resp' =
315+ `combineAction` Action a' c' h' p' f' n' m' ts' body' resp' =
300− Action (a <> a') (c <> c') (h <> h') (p <> p') (n <> n') (m <> m') (ts <> ts') (body <> body') (resp `combineResponse` resp')
316+ Action (a <> a') (c <> c') (h <> h') (p <> p') (f `combineFragment` f') (n <> n') (m <> m') (ts <> ts') (body <> body') (resp `combineResponse` resp')
302-- | Default 'Action'. Has no 'captures', no query 'params', expects
318-- | Default 'Action'. Has no 'captures', no query 'params', expects
303-- no request body ('rqbody') and the typical response is 'defResponse'.
319-- no request body ('rqbody') and the typical response is 'defResponse'.
@@ -305,10 +321,10 @@ Action a c h p n m ts body resp `combineAction` Action a' c' h' p' n' m' ts' bod
305-- Tweakable with lenses.
321-- Tweakable with lenses.
307-- >>> defAction
323-- >>> defAction
308−-- Action {_authInfo = [], _captures = [], _headers = [], _params = [], _notes = [], _mxParams = [], _rqtypes = [], _rqbody = [], _response = Response {_respStatus = 200, _respTypes = [], _respBody = [], _respHeaders = []}}
324+-- Action {_authInfo = [], _captures = [], _headers = [], _params = [], _fragment = Nothing, _notes = [], _mxParams = [], _rqtypes = [], _rqbody = [], _response = Response {_respStatus = 200, _respTypes = [], _respBody = [], _respHeaders = []}}
310-- >>> defAction & response.respStatus .~ 201
326-- >>> defAction & response.respStatus .~ 201
311−-- Action {_authInfo = [], _captures = [], _headers = [], _params = [], _notes = [], _mxParams = [], _rqtypes = [], _rqbody = [], _response = Response {_respStatus = 201, _respTypes = [], _respBody = [], _respHeaders = []}}
327+-- Action {_authInfo = [], _captures = [], _headers = [], _params = [], _fragment = Nothing, _notes = [], _mxParams = [], _rqtypes = [], _rqbody = [], _response = Response {_respStatus = 201, _respTypes = [], _respBody = [], _respHeaders = []}}
313defAction :: Action
329defAction :: Action
314defAction =
330defAction =
@@ -316,6 +332,7 @@ defAction =
@@ -368,6 +385,7 @@ makeLenses ''API
368makeLenses ''Endpoint
385makeLenses ''Endpoint
369makeLenses ''DocCapture
386makeLenses ''DocCapture
370makeLenses ''DocQueryParam
387makeLenses ''DocQueryParam
388+makeLenses ''DocFragment
371makeLenses ''DocIntro
389makeLenses ''DocIntro
372makeLenses ''DocNote
390makeLenses ''DocNote
373makeLenses ''Response
391makeLenses ''Response
@@ -587,6 +605,15 @@ class ToCapture c where
587class ToAuthInfo a where
605class ToAuthInfo a where
588 toAuthInfo :: Proxy a -> DocAuthentication
606 toAuthInfo :: Proxy a -> DocAuthentication
608+-- | The class that helps us get documentation for URL fragments.
610+-- Example of an instance:
612+-- > instance ToFragment (Fragment a) where
613+-- > toFragment _ = DocFragment "fragment" "fragment description"
614+class ToFragment t where
615+ toFragment :: Proxy t -> DocFragment
590-- | Generate documentation in Markdown format for
617-- | Generate documentation in Markdown format for
591-- the given 'API'.
618-- the given 'API'.
@@ -629,6 +656,7 @@ markdownWith RenderingOptions{..} api = unlines $
629 capturesStr (action ^. captures) ++
656 capturesStr (action ^. captures) ++
630 headersStr (action ^. headers) ++
657 headersStr (action ^. headers) ++
631 paramsStr meth (action ^. params) ++
658 paramsStr meth (action ^. params) ++
659+ fragmentStr (action ^. fragment) ++
632 rqbodyStr (action ^. rqtypes) (action ^. rqbody) ++
660 rqbodyStr (action ^. rqtypes) (action ^. rqbody) ++
633 responseStr (action ^. response) ++
661 responseStr (action ^. response) ++
@@ -730,6 +758,14 @@ markdownWith RenderingOptions{..} api = unlines $
731 where values = param ^. paramValues
759 where values = param ^. paramValues
761+ fragmentStr :: Maybe DocFragment -> [String]
762+ fragmentStr Nothing = []
763+ fragmentStr (Just frag) =
764+ [ "### Fragment:", ""
765+ , "- *" ++ (frag ^. fragSymbol) ++ "*: " ++ (frag ^. fragDesc)
733 rqbodyStr :: [M.MediaType] -> [(Text, M.MediaType, ByteString)]-> [String]
769 rqbodyStr :: [M.MediaType] -> [(Text, M.MediaType, ByteString)]-> [String]
734 rqbodyStr [] [] = []
770 rqbodyStr [] [] = []
735 rqbodyStr types s =
771 rqbodyStr types s =
@@ -959,6 +995,15 @@ instance (KnownSymbol sym, ToParam (QueryFlag sym), HasDocs api)
959 paramP = Proxy :: Proxy (QueryFlag sym)
995 paramP = Proxy :: Proxy (QueryFlag sym)
960 action' = over params (|> toParam paramP) action
996 action' = over params (|> toParam paramP) action
998+instance (ToFragment (Fragment a), HasDocs api)
999+ => HasDocs (Fragment a :> api) where
1001+ docsFor Proxy (endpoint, action) =
1002+ docsFor subApiP (endpoint, action')
1004+ where subApiP = Proxy :: Proxy api
1005+ fragmentP = Proxy :: Proxy (Fragment a)
1006+ action' = set fragment (Just (toFragment fragmentP)) action
963instance HasDocs Raw where
1008instance HasDocs Raw where
964 docsFor _proxy (endpoint, action) _ =
1009 docsFor _proxy (endpoint, action) _ =