on master help sign in
Remove Matrix params.

For servant-docs, -foreign, -js, and -mock.

commitec55f4b981f81ca536913e1538cb1ef0959dd3b9 authorJulian K. Arni <jkarni@gmail.com> dateThu 8 Oct 2015, 23:33 (11 years ago) parentafc76b8
7 files changed+56−290
servant-docs/example/greet.hs+1−8 servant-docs/example/greet.md+34−33 servant-docs/src/Servant/Docs.hs+1−120 servant-docs/src/Servant/Docs/Internal.hs+1−59 servant-foreign/src/Servant/Foreign.hs+15−50 servant-js/src/Servant/JS/Internal.hs+4−9 servant-mock/src/Servant/Mock.hs−11
servant-docs/example/greet.hs+1−8
@@ -46,13 +46,6 @@ instance ToParam (QueryParam "capital" Bool) where
4646 \Default is false."
4747 Normal
4848
49−instance ToParam (MatrixParam "lang" String) where
50− toParam _ =
51− DocQueryParam "lang"
52− ["en", "sv", "fr"]
53− "Get the greeting message selected language. Default is en."
54− Normal
55−
5649instance ToSample Greet where
5750 toSamples _ =
5851 [ ("If you use ?capital=true", Greet "HELLO, HASKELLER")
@@ -81,7 +74,7 @@ intro2 = DocIntro "This title is below the last"
8174-- API specification
8275type TestApi =
8376 -- GET /hello/:name?capital={true, false} returns a Greet as JSON or PlainText
84− "hello" :> MatrixParam "lang" String :> Capture "name" Text :> QueryParam "capital" Bool :> Get '[JSON, PlainText] Greet
77+ "hello" :> Capture "name" Text :> QueryParam "capital" Bool :> Get '[JSON, PlainText] Greet
8578
8679 -- POST /greet with a Greet as JSON in the request body,
8780 -- returns a Greet as JSON
servant-docs/example/greet.md+34−33
@@ -1,10 +1,10 @@
1−#### On proper introductions.
1+## On proper introductions.
22
33Hello there.
44
55As documentation is usually written for humans, it's often useful to introduce concepts with a few words.
66
7−#### This title is below the last
7+## This title is below the last
88
99You'll also note that multiple intros are possible.
1010
@@ -19,12 +19,13 @@ You'll also note that multiple intros are possible.
1919- Example: `application/json`
2020
2121```javascript
22−"Hello, haskeller!"
22+"HELLO, HASKELLER"
2323```
2424
2525#### Response:
2626
2727- Status code 201
28+- Headers: [("X-Example","1729")]
2829
2930- Supported content types are:
3031
@@ -42,21 +43,43 @@ You'll also note that multiple intros are possible.
4243"Hello, haskeller"
4344```
4445
45−## GET /hello;lang=<value>/:name
46+## DELETE /greet/:greetid
47+
48+#### Title
49+
50+This is some text
51+
52+#### Second secton
53+
54+And some more
4655
4756#### Captures:
4857
49−- *name*: name of the person to greet
58+- *greetid*: identifier of the greet msg to remove
5059
51−#### Matrix Parameters:
5260
53−**hello**:
61+- This endpoint is sensitive to the value of the **unicorns** HTTP header.
5462
55−- lang
56− - **Values**: *en, sv, fr*
57− - **Description**: Get the greeting message selected language. Default is en.
63+#### Response:
64+
65+- Status code 200
66+- Headers: []
67+
68+- Supported content types are:
69+
70+ - `application/json`
71+
72+- Response body as below.
73+
74+```javascript
75+[]
76+```
5877
78+## GET /hello/:name
5979
80+#### Captures:
81+
82+- *name*: name of the person to greet
6083
6184#### GET Parameters:
6285
@@ -68,6 +91,7 @@ You'll also note that multiple intros are possible.
6891#### Response:
6992
7093- Status code 200
94+- Headers: []
7195
7296- Supported content types are:
7397
@@ -98,27 +122,4 @@ You'll also note that multiple intros are possible.
98122"Hello, haskeller"
99123```
100124
101−## DELETE /greet/:greetid
102−
103−#### Title
104−
105−This is some text
106−
107−#### Second secton
108−
109−And some more
110−
111−#### Captures:
112−
113−- *greetid*: identifier of the greet msg to remove
114−
115−
116−- This endpoint is sensitive to the value of the **unicorns** HTTP header.
117−
118−#### Response:
119−
120−- Status code 200
121−
122−- No response body
123−
124125
servant-docs/src/Servant/Docs.hs+1−120
@@ -20,126 +20,7 @@
2020-- The only thing you'll need to do will be to implement some classes
2121-- for your captures, get parameters and request or response bodies.
2222--
23−-- Here is a complete example that you can run to see the markdown pretty
24−-- printer in action:
25−--
26−-- > {-# LANGUAGE DataKinds #-}
27−-- > {-# LANGUAGE DeriveGeneric #-}
28−-- > {-# LANGUAGE FlexibleInstances #-}
29−-- > {-# LANGUAGE MultiParamTypeClasses #-}
30−-- > {-# LANGUAGE OverloadedStrings #-}
31−-- > {-# LANGUAGE TypeOperators #-}
32−-- > {-# OPTIONS_GHC -fno-warn-orphans #-}
33−-- > import Control.Lens
34−-- > import Data.Aeson
35−-- > import Data.Proxy
36−-- > import Data.String.Conversions
37−-- > import Data.Text (Text)
38−-- > import GHC.Generics
39−-- > import Servant.API
40−-- > import Servant.Docs
41−-- >
42−-- > -- * Example
43−-- >
44−-- > -- | A greet message data type
45−-- > newtype Greet = Greet Text
46−-- > deriving (Generic, Show)
47−-- >
48−-- > -- | We can get JSON support automatically. This will be used to parse
49−-- > -- and encode a Greeting as 'JSON'.
50−-- > instance FromJSON Greet
51−-- > instance ToJSON Greet
52−-- >
53−-- > -- | We can also implement 'MimeRender' for additional formats like 'PlainText'.
54−-- > instance MimeRender PlainText Greet where
55−-- > mimeRender Proxy (Greet s) = "\"" <> cs s <> "\""
56−-- >
57−-- > -- We add some useful annotations to our captures,
58−-- > -- query parameters and request body to make the docs
59−-- > -- really helpful.
60−-- > instance ToCapture (Capture "name" Text) where
61−-- > toCapture _ = DocCapture "name" "name of the person to greet"
62−-- >
63−-- > instance ToCapture (Capture "greetid" Text) where
64−-- > toCapture _ = DocCapture "greetid" "identifier of the greet msg to remove"
65−-- >
66−-- > instance ToParam (QueryParam "capital" Bool) where
67−-- > toParam _ =
68−-- > DocQueryParam "capital"
69−-- > ["true", "false"]
70−-- > "Get the greeting message in uppercase (true) or not (false).\
71−-- > \Default is false."
72−-- > Normal
73−-- >
74−-- > instance ToParam (MatrixParam "lang" String) where
75−-- > toParam _ =
76−-- > DocQueryParam "lang"
77−-- > ["en", "sv", "fr"]
78−-- > "Get the greeting message selected language. Default is en."
79−-- > Normal
80−-- >
81−-- > instance ToSample Greet where
82−-- > toSample _ = Just $ Greet "Hello, haskeller!"
83−-- >
84−-- > toSamples _ =
85−-- > [ ("If you use ?capital=true", Greet "HELLO, HASKELLER")
86−-- > , ("If you use ?capital=false", Greet "Hello, haskeller")
87−-- > ]
88−-- >
89−-- > -- We define some introductory sections, these will appear at the top of the
90−-- > -- documentation.
91−-- > --
92−-- > -- We pass them in with 'docsWith', below. If you only want to add
93−-- > -- introductions, you may use 'docsWithIntros'
94−-- > intro1 :: DocIntro
95−-- > intro1 = DocIntro "On proper introductions." -- The title
96−-- > [ "Hello there."
97−-- > , "As documentation is usually written for humans, it's often useful \
98−-- > \to introduce concepts with a few words." ] -- Elements are paragraphs
99−-- >
100−-- > intro2 :: DocIntro
101−-- > intro2 = DocIntro "This title is below the last"
102−-- > [ "You'll also note that multiple intros are possible." ]
103−-- >
104−-- >
105−-- > -- API specification
106−-- > type TestApi =
107−-- > -- GET /hello/:name?capital={true, false} returns a Greet as JSON or PlainText
108−-- > "hello" :> MatrixParam "lang" String :> Capture "name" Text :> QueryParam "capital" Bool :> Get '[JSON, PlainText] Greet
109−-- >
110−-- > -- POST /greet with a Greet as JSON in the request body,
111−-- > -- returns a Greet as JSON
112−-- > :<|> "greet" :> ReqBody '[JSON] Greet :> Post '[JSON] Greet
113−-- >
114−-- > -- DELETE /greet/:greetid
115−-- > :<|> "greet" :> Capture "greetid" Text :> Delete '[JSON] ()
116−-- >
117−-- > testApi :: Proxy TestApi
118−-- > testApi = Proxy
119−-- >
120−-- > -- Build some extra information for the DELETE /greet/:greetid endpoint. We
121−-- > -- want to add documentation about a secret unicorn header and some extra
122−-- > -- notes.
123−-- > extra :: ExtraInfo TestApi
124−-- > extra =
125−-- > extraInfo (Proxy :: Proxy ("greet" :> Capture "greetid" Text :> Delete '[JSON] ())) $
126−-- > defAction & headers <>~ ["unicorns"]
127−-- > & notes <>~ [ DocNote "Title" ["This is some text"]
128−-- > , DocNote "Second secton" ["And some more"]
129−-- > ]
130−-- >
131−-- > -- Generate the data that lets us have API docs. This
132−-- > -- is derived from the type as well as from
133−-- > -- the 'ToCapture', 'ToParam' and 'ToSample' instances from above.
134−-- > --
135−-- > -- If you didn't want intros and extra information, you could just call:
136−-- > --
137−-- > -- > docs testAPI :: API
138−-- > docsGreet :: API
139−-- > docsGreet = docsWith [intro1, intro2] extra testApi
140−-- >
141−-- > main :: IO ()
142−-- > main = putStrLn $ markdown docsGreet
23+-- See example/greet.hs for an example.
14324module Servant.Docs
14425 ( -- * 'HasDocs' class and key functions
14526 HasDocs(..), docs, markdown
servant-docs/src/Servant/Docs/Internal.hs+1−59
@@ -24,8 +24,7 @@ module Servant.Docs.Internal where
2424import Control.Applicative
2525import Control.Arrow (second)
2626import Control.Lens (makeLenses, over, traversed, (%~),
27− (&), (.~), (<>~), (^.), _1, _2,
28− _last, (|>))
27+ (&), (.~), (<>~), (^.), (|>))
2928import qualified Control.Monad.Omega as Omega
3029import Data.ByteString.Conversion (ToByteString, toByteString)
3130import Data.ByteString.Lazy.Char8 (ByteString)
@@ -546,7 +545,6 @@ markdown api = unlines $
546545 "" :
547546 notesStr (action ^. notes) ++
548547 capturesStr (action ^. captures) ++
549− mxParamsStr (action ^. mxParams) ++
550548 headersStr (action ^. headers) ++
551549 paramsStr (action ^. params) ++
552550 rqbodyStr (action ^. rqtypes) (action ^. rqbody) ++
@@ -590,20 +588,6 @@ markdown api = unlines $
590588 captureStr cap =
591589 "- *" ++ (cap ^. capSymbol) ++ "*: " ++ (cap ^. capDesc)
592590
593− mxParamsStr :: [(String, [DocQueryParam])] -> [String]
594− mxParamsStr [] = []
595− mxParamsStr l =
596− "#### Matrix Parameters:" :
597− "" :
598− map segmentStr l
599− segmentStr :: (String, [DocQueryParam]) -> String
600− segmentStr (segment, l) = unlines $
601− ("**" ++ segment ++ "**:") :
602− "" :
603− map paramStr l ++
604− "" :
605− []
606−
607591 headersStr :: [Text] -> [String]
608592 headersStr [] = []
609593 headersStr l = [""] ++ map headerStr l ++ [""]
@@ -898,48 +882,6 @@ instance (KnownSymbol sym, ToParam (QueryFlag sym), HasDocs sublayout)
898882 action' = over params (|> toParam paramP) action
899883
900884
901−instance (KnownSymbol sym, ToParam (MatrixParam sym a), HasDocs sublayout)
902− => HasDocs (MatrixParam sym a :> sublayout) where
903−
904− docsFor Proxy (endpoint, action) =
905− docsFor sublayoutP (endpoint', action')
906−
907− where sublayoutP = Proxy :: Proxy sublayout
908− paramP = Proxy :: Proxy (MatrixParam sym a)
909− segment = endpoint ^. (path._last)
910− segment' = action ^. (mxParams._last._1)
911− endpoint' = over (path._last) (\p -> p ++ ";" ++ symbolVal symP ++ "=<value>") endpoint
912−
913− action' = if segment' /= segment
914− -- This is the first matrix parameter for this segment, insert a new entry into the mxParams list
915− then over mxParams (|> (segment, [toParam paramP])) action
916− -- We've already inserted a matrix parameter for this segment, append to the existing list
917− else action & mxParams._last._2 <>~ [toParam paramP]
918− symP = Proxy :: Proxy sym
919−
920−
921−instance (KnownSymbol sym, {- ToParam (MatrixParams sym a), -} HasDocs sublayout)
922− => HasDocs (MatrixParams sym a :> sublayout) where
923−
924− docsFor Proxy (endpoint, action) =
925− docsFor sublayoutP (endpoint', action)
926−
927− where sublayoutP = Proxy :: Proxy sublayout
928− endpoint' = over path (\p -> p ++ [";" ++ symbolVal symP ++ "=<value>"]) endpoint
929− symP = Proxy :: Proxy sym
930−
931−
932−instance (KnownSymbol sym, {- ToParam (MatrixFlag sym), -} HasDocs sublayout)
933− => HasDocs (MatrixFlag sym :> sublayout) where
934−
935− docsFor Proxy (endpoint, action) =
936− docsFor sublayoutP (endpoint', action)
937−
938− where sublayoutP = Proxy :: Proxy sublayout
939−
940− endpoint' = over path (\p -> p ++ [";" ++ symbolVal symP]) endpoint
941− symP = Proxy :: Proxy sym
942−
943885instance HasDocs Raw where
944886 docsFor _proxy (endpoint, action) _ =
945887 single endpoint action
servant-foreign/src/Servant/Foreign.hs+15−50
@@ -20,7 +20,6 @@ module Servant.Foreign
2020 ( HasForeign(..)
2121 , Segment(..)
2222 , SegmentType(..)
23− , MatrixArg
2423 , FunctionName
2524 , QueryArg(..)
2625 , HeaderArg(..)
@@ -47,15 +46,14 @@ module Servant.Foreign
4746 , module Servant.API
4847 ) where
4948
50−import Control.Lens (makeLenses, (%~), (&), (.~),
51− (<>~), _last)
52−import Data.Monoid ((<>))
53−import Data.Text
54−import Data.Proxy
55−import GHC.Exts (Constraint)
56−import GHC.TypeLits
57−import Servant.API
58−import Prelude hiding (concat)
49+import Control.Lens (makeLenses, (%~), (&), (.~), (<>~))
50+import Data.Monoid ((<>))
51+import Data.Proxy
52+import Data.Text
53+import GHC.Exts (Constraint)
54+import GHC.TypeLits
55+import Prelude hiding (concat)
56+import Servant.API
5957
6058-- | Function name builder that simply concat each part together
6159concatCase :: FunctionName -> Text
@@ -76,7 +74,7 @@ camelCase (p:ps) = concat $ p : camelCase' ps
7674
7775type Arg = Text
7876
79−data Segment = Segment { _segment :: SegmentType, _matrix :: [MatrixArg] }
77+newtype Segment = Segment { _segment :: SegmentType }
8078 deriving (Eq, Show)
8179
8280data SegmentType = Static Text -- ^ a static path segment. like "/foo"
@@ -105,8 +103,6 @@ data HeaderArg = HeaderArg
105103 } deriving (Eq, Show)
106104
107105
108−type MatrixArg = QueryArg
109−
110106data Url = Url
111107 { _path :: Path
112108 , _queryStr :: [QueryArg]
@@ -132,12 +128,12 @@ makeLenses ''Url
132128makeLenses ''Req
133129
134130isCapture :: Segment -> Bool
135−isCapture (Segment (Cap _) _) = True
136−isCapture _ = False
131+isCapture (Segment (Cap _)) = True
132+isCapture _ = False
137133
138134captureArg :: Segment -> Arg
139−captureArg (Segment (Cap s) _) = s
140−captureArg _ = error "captureArg called on non capture"
135+captureArg (Segment (Cap s)) = s
136+captureArg _ = error "captureArg called on non capture"
141137
142138defReq :: Req
143139defReq = Req defUrl "GET" [] False []
@@ -169,7 +165,7 @@ instance (KnownSymbol sym, HasForeign sublayout)
169165
170166 foreignFor Proxy req =
171167 foreignFor (Proxy :: Proxy sublayout) $
172− req & reqUrl.path <>~ [Segment (Cap str) []]
168+ req & reqUrl.path <>~ [Segment (Cap str)]
173169 & funcName %~ (++ ["by", str])
174170
175171 where str = pack . symbolVal $ (Proxy :: Proxy sym)
@@ -242,37 +238,6 @@ instance (KnownSymbol sym, HasForeign sublayout)
242238
243239 where str = pack . symbolVal $ (Proxy :: Proxy sym)
244240
245−instance (KnownSymbol sym, HasForeign sublayout)
246− => HasForeign (MatrixParam sym a :> sublayout) where
247− type Foreign (MatrixParam sym a :> sublayout) = Foreign sublayout
248−
249− foreignFor Proxy req =
250− foreignFor (Proxy :: Proxy sublayout) $
251− req & reqUrl.path._last.matrix <>~ [QueryArg strArg Normal]
252−
253− where str = pack . symbolVal $ (Proxy :: Proxy sym)
254− strArg = str <> "Value"
255−
256−instance (KnownSymbol sym, HasForeign sublayout)
257− => HasForeign (MatrixParams sym a :> sublayout) where
258− type Foreign (MatrixParams sym a :> sublayout) = Foreign sublayout
259−
260− foreignFor Proxy req =
261− foreignFor (Proxy :: Proxy sublayout) $
262− req & reqUrl.path._last.matrix <>~ [QueryArg str List]
263−
264− where str = pack . symbolVal $ (Proxy :: Proxy sym)
265−
266−instance (KnownSymbol sym, HasForeign sublayout)
267− => HasForeign (MatrixFlag sym :> sublayout) where
268− type Foreign (MatrixFlag sym :> sublayout) = Foreign sublayout
269−
270− foreignFor Proxy req =
271− foreignFor (Proxy :: Proxy sublayout) $
272− req & reqUrl.path._last.matrix <>~ [QueryArg str Flag]
273−
274− where str = pack . symbolVal $ (Proxy :: Proxy sym)
275−
276241instance HasForeign Raw where
277242 type Foreign Raw = Method -> Req
278243
@@ -293,7 +258,7 @@ instance (KnownSymbol path, HasForeign sublayout)
293258
294259 foreignFor Proxy req =
295260 foreignFor (Proxy :: Proxy sublayout) $
296− req & reqUrl.path <>~ [Segment (Static str) []]
261+ req & reqUrl.path <>~ [Segment (Static str)]
297262 & funcName %~ (++ [str])
298263
299264 where str = Data.Text.map (\c -> if c == '.' then '_' else c) . pack . symbolVal $ (Proxy :: Proxy path)
servant-js/src/Servant/JS/Internal.hs+4−9
@@ -9,7 +9,6 @@ module Servant.JS.Internal
99 , segmentTypeToStr
1010 , jsParams
1111 , jsGParams
12− , jsMParams
1312 , paramToStr
1413 , toValidFunctionName
1514 , toJSHeader
@@ -92,7 +91,7 @@ toValidFunctionName :: Text -> Text
9291toValidFunctionName t =
9392 case T.uncons t of
9493 Just (x,xs) ->
95− setFirstChar x `T.cons` T.filter remainder xs
94+ setFirstChar x `T.cons` T.filter remainder xs
9695 Nothing -> "_"
9796 where
9897 setFirstChar c = if firstChar c then c else '_'
@@ -105,7 +104,7 @@ toValidFunctionName t =
105104 , Set.titlecaseLetter
106105 , Set.modifierLetter
107106 , Set.otherLetter
108− , Set.letterNumber
107+ , Set.letterNumber
109108 ]
110109 remainderOK = firstLetterOK
111110 <> mconcat
@@ -134,8 +133,8 @@ jsSegments [x] = "/" <> segmentToStr x False
134133jsSegments (x:xs) = "/" <> segmentToStr x True <> jsSegments xs
135134
136135segmentToStr :: Segment -> Bool -> Text
137−segmentToStr (Segment st ms) notTheEnd =
138− segmentTypeToStr st <> jsMParams ms <> if notTheEnd then "" else "'"
136+segmentToStr (Segment st) notTheEnd =
137+ segmentTypeToStr st <> if notTheEnd then "" else "'"
139138
140139segmentTypeToStr :: SegmentType -> Text
141140segmentTypeToStr (Static s) = s
@@ -149,10 +148,6 @@ jsGParams s (x:xs) = paramToStr x True <> s <> jsGParams s xs
149148jsParams :: [QueryArg] -> Text
150149jsParams = jsGParams "&"
151150
152−jsMParams :: [MatrixArg] -> Text
153−jsMParams [] = ""
154−jsMParams xs = ";" <> jsGParams ";" xs
155−
156151paramToStr :: QueryArg -> Bool -> Text
157152paramToStr qarg notTheEnd =
158153 case qarg ^. argType of
servant-mock/src/Servant/Mock.hs−11
@@ -136,17 +136,6 @@ instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
136136instance (KnownSymbol s, HasMock rest) => HasMock (QueryFlag s :> rest) where
137137 mock _ = \_ -> mock (Proxy :: Proxy rest)
138138
139−instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
140− => HasMock (MatrixParam s a :> rest) where
141− mock _ = \_ -> mock (Proxy :: Proxy rest)
142−
143−instance (KnownSymbol s, FromHttpApiData a, HasMock rest)
144− => HasMock (MatrixParams s a :> rest) where
145− mock _ = \_ -> mock (Proxy :: Proxy rest)
146−
147−instance (KnownSymbol s, HasMock rest) => HasMock (MatrixFlag s :> rest) where
148− mock _ = \_ -> mock (Proxy :: Proxy rest)
149−
150139instance (KnownSymbol h, FromHttpApiData a, HasMock rest) => HasMock (Header h a :> rest) where
151140 mock _ = \_ -> mock (Proxy :: Proxy rest)
152141