on master help sign in
Added test cases for matrix parameters. Added some missing cases in the IsElem type family (QueryParams and QueryFlag)
0b2f2aeDaniel Larsson
src/Servant/QQ.hs+30−16
@@ -58,6 +58,7 @@ import Servant.API.Post ( Post )
58import Servant.API.Put ( Put )
58import Servant.API.Put ( Put )
59import Servant.API.Delete ( Delete )
59import Servant.API.Delete ( Delete )
60import Servant.API.QueryParam ( QueryParam )
60import Servant.API.QueryParam ( QueryParam )
61+import Servant.API.MatrixParam ( MatrixParam )
61import Servant.API.ReqBody ( ReqBody )
62import Servant.API.ReqBody ( ReqBody )
62import Servant.API.Sub ( (:>) )
63import Servant.API.Sub ( (:>) )
63import Servant.API.Alternative ( (:<|>) )
64import Servant.API.Alternative ( (:<|>) )
@@ -68,15 +69,16 @@ import Servant.API.Alternative ( (:<|>) )
68-- only one of 'get', 'post', 'put', and 'delete' in a value), but
69-- only one of 'get', 'post', 'put', and 'delete' in a value), but
69-- sometimes requires a little more work.
70-- sometimes requires a little more work.
70class ExpSYM repr' repr | repr -> repr', repr' -> repr where
71class ExpSYM repr' repr | repr -> repr', repr' -> repr where
71− lit :: String -> repr' -> repr
72+ lit :: String -> repr' -> repr
72− capture :: String -> String -> repr -> repr
73+ capture :: String -> String -> repr -> repr
73− reqBody :: String -> repr -> repr
74+ reqBody :: String -> repr -> repr
75+ queryParam :: String -> String -> repr -> repr
74− queryParam :: String -> String -> repr -> repr
76+ matrixParam :: String -> String -> repr -> repr
75− conj :: repr' -> repr -> repr
77+ conj :: repr' -> repr -> repr
76− get :: String -> repr
78+ get :: String -> repr
77− post :: String -> repr
79+ post :: String -> repr
78− put :: String -> repr
80+ put :: String -> repr
79− delete :: String -> repr
81+ delete :: String -> repr
80
82
81
83
82infixr 6 >:
84infixr 6 >:
@@ -90,7 +92,9 @@ instance ExpSYM Type Type where
90 capture name typ r = AppT (AppT (ConT ''Capture) (LitT (StrTyLit name)))
92 capture name typ r = AppT (AppT (ConT ''Capture) (LitT (StrTyLit name)))
91 (ConT $ mkName typ) >: r
93 (ConT $ mkName typ) >: r
92 reqBody typ r = AppT (ConT ''ReqBody) (ConT $ mkName typ) >: r
94 reqBody typ r = AppT (ConT ''ReqBody) (ConT $ mkName typ) >: r
93− queryParam name typ r = AppT (AppT (ConT ''QueryParam) (LitT (StrTyLit name)))
95+ queryParam name typ r = AppT (AppT (ConT ''QueryParam) (LitT (StrTyLit name)))
96+ (ConT $ mkName typ) >: r
97+ matrixParam name typ r = AppT (AppT (ConT ''MatrixParam) (LitT (StrTyLit name)))
94 (ConT $ mkName typ) >: r
98 (ConT $ mkName typ) >: r
95 conj x = AppT (AppT (ConT ''(:>)) x)
99 conj x = AppT (AppT (ConT ''(:>)) x)
96 get typ = AppT (ConT ''Get) (ConT $ mkName typ)
100 get typ = AppT (ConT ''Get) (ConT $ mkName typ)
@@ -111,17 +115,27 @@ parseUrlSegment = try parseCapture
111 <|> try parseLit
115 <|> try parseLit
112 where
116 where
113 parseCapture = do
117 parseCapture = do
114− cname <- many (noneOf " ?/:")
118+ cname <- many (noneOf " ?/:;")
115 char ':'
119 char ':'
116− ctyp <- many (noneOf " ?/:")
120+ ctyp <- many (noneOf " ?/:;")
121+ mx <- many parseMatrixParam
117− return $ capture cname ctyp
122+ return $ capture cname ctyp . foldr (.) id mx
118 parseQueryParam = do
123 parseQueryParam = do
119 char '?'
124 char '?'
120− cname <- many (noneOf " ?/:")
125+ cname <- many (noneOf " ?/:;")
121 char ':'
126 char ':'
122− ctyp <- many (noneOf " ?/:")
127+ ctyp <- many (noneOf " ?/:;")
123 return $ queryParam cname ctyp
128 return $ queryParam cname ctyp
129+ parseLit = do
130+ lt <- many (noneOf " ?/:;")
131+ mx <- many parseMatrixParam
132+ return $ lit lt . foldr (.) id mx
133+ parseMatrixParam = do
134+ char ';'
124− parseLit = lit <$> many (noneOf " ?/:")
135+ cname <- many (noneOf " ?/:;")
136+ char ':'
137+ ctyp <- many (noneOf " ?/:;")
138+ return $ matrixParam cname ctyp
125
139
126parseUrl :: ExpSYM repr repr => Parser (repr -> repr)
140parseUrl :: ExpSYM repr repr => Parser (repr -> repr)
127parseUrl = do
141parseUrl = do
src/Servant/Utils/Links.hs+6
@@ -54,6 +54,7 @@ import GHC.TypeLits ( KnownSymbol, Symbol, symbolVal )
54import Servant.API.Capture ( Capture )
54import Servant.API.Capture ( Capture )
55import Servant.API.ReqBody ( ReqBody )
55import Servant.API.ReqBody ( ReqBody )
56import Servant.API.QueryParam ( QueryParam )
56import Servant.API.QueryParam ( QueryParam )
57+import Servant.API.MatrixParam ( MatrixParam )
57import Servant.API.Get ( Get )
58import Servant.API.Get ( Get )
58import Servant.API.Post ( Post )
59import Servant.API.Post ( Post )
59import Servant.API.Put ( Put )
60import Servant.API.Put ( Put )
@@ -78,6 +79,11 @@ type family IsElem a s where
78 IsElem (e :> sa) (Capture x y :> sb) = IsElem sa sb
79 IsElem (e :> sa) (Capture x y :> sb) = IsElem sa sb
79 IsElem sa (ReqBody x :> sb) = IsElem sa sb
80 IsElem sa (ReqBody x :> sb) = IsElem sa sb
80 IsElem sa (QueryParam x y :> sb) = IsElem sa sb
81 IsElem sa (QueryParam x y :> sb) = IsElem sa sb
82+ IsElem sa (QueryParams x y :> sb) = IsElem sa sb
83+ IsElem sa (QueryFlag x :> sb) = IsElem sa sb
84+ IsElem sa (MatrixParam x y :> sb) = IsElem sa sb
85+ IsElem sa (MatrixParams x y :> sb) = IsElem sa sb
86+ IsElem sa (MatrixFlag x :> sb) = IsElem sa sb
81 IsElem e e = 'True
87 IsElem e e = 'True
82 IsElem e a = 'False
88 IsElem e a = 'False
83
89
test/Servant/QQSpec.hs+22
@@ -85,6 +85,20 @@ type SimpleQueryParam' = "hello" :> QueryParam "p" Int :> Post Bool
85type SimpleQueryParam'' = "hello" :> QueryParam "r" Int :> Post Bool
85type SimpleQueryParam'' = "hello" :> QueryParam "r" Int :> Post Bool
86type SimpleQueryParam''' = "hello" :> QueryParam "p" Bool :> Post Bool
86type SimpleQueryParam''' = "hello" :> QueryParam "p" Bool :> Post Bool
87
87
88+type SimpleMatrixParam = [sitemap|
89+POST hello;p:Int Bool
90+|]
91+type SimpleMatrixParam' = "hello" :> MatrixParam "p" Int :> Post Bool
92+type SimpleMatrixParam'' = "hello" :> MatrixParam "r" Int :> Post Bool
93+type SimpleMatrixParam''' = "hello" :> MatrixParam "p" Bool :> Post Bool
94+
95+type ComplexMatrixParam = [sitemap|
96+POST hello;p:Int;q:String/world;r:Int Bool
97+|]
98+type ComplexMatrixParam' = "hello" :> MatrixParam "p" Int :> MatrixParam "q" String :> "world" :> MatrixParam "r" Int :> Post Bool
99+type ComplexMatrixParam'' = "hello" :> MatrixParam "p" Int :> MatrixParam "q" String :> "world" :> MatrixParam "s" Int :> Post Bool
100+type ComplexMatrixParam''' = "hello" :> MatrixParam "p" Int :> MatrixParam "q" String :> "world" :> MatrixParam "r" Bool :> Post Bool
101+
88-- Combinations ----------------------------------------------------------
102-- Combinations ----------------------------------------------------------
89
103
90type TwoPaths = [sitemap|
104type TwoPaths = [sitemap|
@@ -153,6 +167,14 @@ spec = do
153 (u::SimpleQueryParam) ~= (u::SimpleQueryParam' ) ~> True
167 (u::SimpleQueryParam) ~= (u::SimpleQueryParam' ) ~> True
154 (u::SimpleQueryParam) ~= (u::SimpleQueryParam'') ~> False
168 (u::SimpleQueryParam) ~= (u::SimpleQueryParam'') ~> False
155 (u::SimpleQueryParam) ~= (u::SimpleQueryParam''') ~> False
169 (u::SimpleQueryParam) ~= (u::SimpleQueryParam''') ~> False
170+ it "Handles simple matrix parameters" $ do
171+ (u::SimpleMatrixParam) ~= (u::SimpleMatrixParam' ) ~> True
172+ (u::SimpleMatrixParam) ~= (u::SimpleMatrixParam'') ~> False
173+ (u::SimpleMatrixParam) ~= (u::SimpleMatrixParam''') ~> False
174+ it "Handles more complex matrix parameters" $ do
175+ (u::ComplexMatrixParam) ~= (u::ComplexMatrixParam' ) ~> True
176+ (u::ComplexMatrixParam) ~= (u::ComplexMatrixParam'') ~> False
177+ (u::ComplexMatrixParam) ~= (u::ComplexMatrixParam''') ~> False
156 it "Handles multiples paths" $ do
178 it "Handles multiples paths" $ do
157 (u::TwoPaths) ~= (u::TwoPaths') ~> True
179 (u::TwoPaths) ~= (u::TwoPaths') ~> True
158 it "Ignores inline comments" $ do
180 it "Ignores inline comments" $ do
test/Servant/Utils/LinksSpec.hs+6
@@ -14,12 +14,15 @@ import Servant.Utils.Links ( IsElem, IsLink )
14type TestApi =
14type TestApi =
15 "hello" :> Capture "name" String :> QueryParam "capital" Bool :> Get Bool
15 "hello" :> Capture "name" String :> QueryParam "capital" Bool :> Get Bool
16 :<|> "greet" :> ReqBody 'True :> Post Bool
16 :<|> "greet" :> ReqBody 'True :> Post Bool
17+ :<|> "parent" :> MatrixParams "name" String :> "child" :> MatrixParam "gender" String :> Get String
17
18
18type TestLink = "hello" :> "hi" :> Get Bool
19type TestLink = "hello" :> "hi" :> Get Bool
19type TestLink2 = "greet" :> Post Bool
20type TestLink2 = "greet" :> Post Bool
21+type TestLink3 = "parent" :> "child" :> Get String
20
22
21type BadTestLink = "hallo" :> "hi" :> Get Bool
23type BadTestLink = "hallo" :> "hi" :> Get Bool
22type BadTestLink2 = "greet" :> Get Bool
24type BadTestLink2 = "greet" :> Get Bool
25+type BadTestLink3 = "parent" :> "child" :> MatrixFlag "male" :> Get String
23
26
24type NotALink = "hello" :> Capture "x" Bool :> Get Bool
27type NotALink = "hello" :> Capture "x" Bool :> Get Bool
25type NotALink2 = "hello" :> ReqBody 'True :> Get Bool
28type NotALink2 = "hello" :> ReqBody 'True :> Get Bool
@@ -39,15 +42,18 @@ isElem = describe "IsElem" $ do
39 it "is True when the first argument is an url within the second" $ do
42 it "is True when the first argument is an url within the second" $ do
40 reflected (Proxy::Proxy (IsElem TestLink TestApi)) ~> True
43 reflected (Proxy::Proxy (IsElem TestLink TestApi)) ~> True
41 reflected (Proxy::Proxy (IsElem TestLink2 TestApi)) ~> True
44 reflected (Proxy::Proxy (IsElem TestLink2 TestApi)) ~> True
45+ reflected (Proxy::Proxy (IsElem TestLink3 TestApi)) ~> True
42 it "is False when the first argument is not an url within the second" $ do
46 it "is False when the first argument is not an url within the second" $ do
43 reflected (Proxy::Proxy (IsElem BadTestLink TestApi)) ~> False
47 reflected (Proxy::Proxy (IsElem BadTestLink TestApi)) ~> False
44 reflected (Proxy::Proxy (IsElem BadTestLink2 TestApi)) ~> False
48 reflected (Proxy::Proxy (IsElem BadTestLink2 TestApi)) ~> False
49+ reflected (Proxy::Proxy (IsElem BadTestLink3 TestApi)) ~> False
45
50
46isLink :: Spec
51isLink :: Spec
47isLink = describe "IsLink" $ do
52isLink = describe "IsLink" $ do
48 it "is True when all Subs are paths and the last is a method" $ do
53 it "is True when all Subs are paths and the last is a method" $ do
49 reflected (Proxy::Proxy (IsLink TestLink)) ~> True
54 reflected (Proxy::Proxy (IsLink TestLink)) ~> True
50 reflected (Proxy::Proxy (IsLink TestLink2)) ~> True
55 reflected (Proxy::Proxy (IsLink TestLink2)) ~> True
56+ reflected (Proxy::Proxy (IsLink TestLink3)) ~> True
51 it "is False of anything with captures" $ do
57 it "is False of anything with captures" $ do
52 reflected (Proxy::Proxy (IsLink NotALink)) ~> False
58 reflected (Proxy::Proxy (IsLink NotALink)) ~> False
53 reflected (Proxy::Proxy (IsLink NotALink2)) ~> False
59 reflected (Proxy::Proxy (IsLink NotALink2)) ~> False