on master help sign in
Added test cases for matrix parameters. Added some missing cases in the IsElem type family (QueryParams and QueryFlag)
409a104Daniel Larsson
src/Servant/QQ.hs+30−16
@@ -40,6 +40,7 @@ import Servant.API.Post
40import Servant.API.Put
40import Servant.API.Put
41import Servant.API.Delete
41import Servant.API.Delete
42import Servant.API.QueryParam
42import Servant.API.QueryParam
43+import Servant.API.MatrixParam
43import Servant.API.ReqBody
44import Servant.API.ReqBody
44import Servant.API.Sub
45import Servant.API.Sub
45import Servant.API.Alternative
46import Servant.API.Alternative
@@ -50,15 +51,16 @@ import Servant.API.Alternative
50-- only one of 'get', 'post', 'put', and 'delete' in a value), but
51-- only one of 'get', 'post', 'put', and 'delete' in a value), but
51-- sometimes requires a little more work.
52-- sometimes requires a little more work.
52class ExpSYM repr' repr | repr -> repr', repr' -> repr where
53class ExpSYM repr' repr | repr -> repr', repr' -> repr where
53− lit :: String -> repr' -> repr
54+ lit :: String -> repr' -> repr
54− capture :: String -> String -> repr -> repr
55+ capture :: String -> String -> repr -> repr
55− reqBody :: String -> repr -> repr
56+ reqBody :: String -> repr -> repr
57+ queryParam :: String -> String -> repr -> repr
56− queryParam :: String -> String -> repr -> repr
58+ matrixParam :: String -> String -> repr -> repr
57− conj :: repr' -> repr -> repr
59+ conj :: repr' -> repr -> repr
58− get :: String -> repr
60+ get :: String -> repr
59− post :: String -> repr
61+ post :: String -> repr
60− put :: String -> repr
62+ put :: String -> repr
61− delete :: String -> repr
63+ delete :: String -> repr
62
64
63
65
64infixr 6 >:
66infixr 6 >:
@@ -72,7 +74,9 @@ instance ExpSYM Type Type where
72 capture name typ r = AppT (AppT (ConT ''Capture) (LitT (StrTyLit name)))
74 capture name typ r = AppT (AppT (ConT ''Capture) (LitT (StrTyLit name)))
73 (ConT $ mkName typ) >: r
75 (ConT $ mkName typ) >: r
74 reqBody typ r = AppT (ConT ''ReqBody) (ConT $ mkName typ) >: r
76 reqBody typ r = AppT (ConT ''ReqBody) (ConT $ mkName typ) >: r
75− queryParam name typ r = AppT (AppT (ConT ''QueryParam) (LitT (StrTyLit name)))
77+ queryParam name typ r = AppT (AppT (ConT ''QueryParam) (LitT (StrTyLit name)))
78+ (ConT $ mkName typ) >: r
79+ matrixParam name typ r = AppT (AppT (ConT ''MatrixParam) (LitT (StrTyLit name)))
76 (ConT $ mkName typ) >: r
80 (ConT $ mkName typ) >: r
77 conj x = AppT (AppT (ConT ''(:>)) x)
81 conj x = AppT (AppT (ConT ''(:>)) x)
78 get typ = AppT (ConT ''Get) (ConT $ mkName typ)
82 get typ = AppT (ConT ''Get) (ConT $ mkName typ)
@@ -93,17 +97,27 @@ parseUrlSegment = try parseCapture
93 <|> try parseLit
97 <|> try parseLit
94 where
98 where
95 parseCapture = do
99 parseCapture = do
96− cname <- many (noneOf " ?/:")
100+ cname <- many (noneOf " ?/:;")
97 char ':'
101 char ':'
98− ctyp <- many (noneOf " ?/:")
102+ ctyp <- many (noneOf " ?/:;")
103+ mx <- many parseMatrixParam
99− return $ capture cname ctyp
104+ return $ capture cname ctyp . foldr (.) id mx
100 parseQueryParam = do
105 parseQueryParam = do
101 char '?'
106 char '?'
102− cname <- many (noneOf " ?/:")
107+ cname <- many (noneOf " ?/:;")
103 char ':'
108 char ':'
104− ctyp <- many (noneOf " ?/:")
109+ ctyp <- many (noneOf " ?/:;")
105 return $ queryParam cname ctyp
110 return $ queryParam cname ctyp
111+ parseLit = do
112+ lt <- many (noneOf " ?/:;")
113+ mx <- many parseMatrixParam
114+ return $ lit lt . foldr (.) id mx
115+ parseMatrixParam = do
116+ char ';'
106− parseLit = lit <$> many (noneOf " ?/:")
117+ cname <- many (noneOf " ?/:;")
118+ char ':'
119+ ctyp <- many (noneOf " ?/:;")
120+ return $ matrixParam cname ctyp
107
121
108parseUrl :: ExpSYM repr repr => Parser (repr -> repr)
122parseUrl :: ExpSYM repr repr => Parser (repr -> repr)
109parseUrl = do
123parseUrl = do
src/Servant/Utils/Links.hs+6
@@ -54,6 +54,7 @@ import GHC.TypeLits
54import Servant.API.Capture
54import Servant.API.Capture
55import Servant.API.ReqBody
55import Servant.API.ReqBody
56import Servant.API.QueryParam
56import Servant.API.QueryParam
57+import Servant.API.MatrixParam
57import Servant.API.Get
58import Servant.API.Get
58import Servant.API.Post
59import Servant.API.Post
59import Servant.API.Put
60import Servant.API.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
@@ -76,6 +76,20 @@ type SimpleQueryParam' = "hello" :> QueryParam "p" Int :> Post Bool
76type SimpleQueryParam'' = "hello" :> QueryParam "r" Int :> Post Bool
76type SimpleQueryParam'' = "hello" :> QueryParam "r" Int :> Post Bool
77type SimpleQueryParam''' = "hello" :> QueryParam "p" Bool :> Post Bool
77type SimpleQueryParam''' = "hello" :> QueryParam "p" Bool :> Post Bool
78
78
79+type SimpleMatrixParam = [sitemap|
80+POST hello;p:Int Bool
81+|]
82+type SimpleMatrixParam' = "hello" :> MatrixParam "p" Int :> Post Bool
83+type SimpleMatrixParam'' = "hello" :> MatrixParam "r" Int :> Post Bool
84+type SimpleMatrixParam''' = "hello" :> MatrixParam "p" Bool :> Post Bool
85+
86+type ComplexMatrixParam = [sitemap|
87+POST hello;p:Int;q:String/world;r:Int Bool
88+|]
89+type ComplexMatrixParam' = "hello" :> MatrixParam "p" Int :> MatrixParam "q" String :> "world" :> MatrixParam "r" Int :> Post Bool
90+type ComplexMatrixParam'' = "hello" :> MatrixParam "p" Int :> MatrixParam "q" String :> "world" :> MatrixParam "s" Int :> Post Bool
91+type ComplexMatrixParam''' = "hello" :> MatrixParam "p" Int :> MatrixParam "q" String :> "world" :> MatrixParam "r" Bool :> Post Bool
92+
79-- Combinations ----------------------------------------------------------
93-- Combinations ----------------------------------------------------------
80
94
81type TwoPaths = [sitemap|
95type TwoPaths = [sitemap|
@@ -144,6 +158,14 @@ spec = do
144 (u::SimpleQueryParam) ~= (u::SimpleQueryParam' ) ~> True
158 (u::SimpleQueryParam) ~= (u::SimpleQueryParam' ) ~> True
145 (u::SimpleQueryParam) ~= (u::SimpleQueryParam'') ~> False
159 (u::SimpleQueryParam) ~= (u::SimpleQueryParam'') ~> False
146 (u::SimpleQueryParam) ~= (u::SimpleQueryParam''') ~> False
160 (u::SimpleQueryParam) ~= (u::SimpleQueryParam''') ~> False
161+ it "Handles simple matrix parameters" $ do
162+ (u::SimpleMatrixParam) ~= (u::SimpleMatrixParam' ) ~> True
163+ (u::SimpleMatrixParam) ~= (u::SimpleMatrixParam'') ~> False
164+ (u::SimpleMatrixParam) ~= (u::SimpleMatrixParam''') ~> False
165+ it "Handles more complex matrix parameters" $ do
166+ (u::ComplexMatrixParam) ~= (u::ComplexMatrixParam' ) ~> True
167+ (u::ComplexMatrixParam) ~= (u::ComplexMatrixParam'') ~> False
168+ (u::ComplexMatrixParam) ~= (u::ComplexMatrixParam''') ~> False
147 it "Handles multiples paths" $ do
169 it "Handles multiples paths" $ do
148 (u::TwoPaths) ~= (u::TwoPaths') ~> True
170 (u::TwoPaths) ~= (u::TwoPaths') ~> True
149 it "Ignores inline comments" $ do
171 it "Ignores inline comments" $ do
test/Servant/Utils/LinksSpec.hs+6
@@ -12,12 +12,15 @@ import Servant.Utils.Links (IsElem, IsLink)
12type TestApi =
12type TestApi =
13 "hello" :> Capture "name" String :> QueryParam "capital" Bool :> Get Bool
13 "hello" :> Capture "name" String :> QueryParam "capital" Bool :> Get Bool
14 :<|> "greet" :> ReqBody 'True :> Post Bool
14 :<|> "greet" :> ReqBody 'True :> Post Bool
15+ :<|> "parent" :> MatrixParams "name" String :> "child" :> MatrixParam "gender" String :> Get String
15
16
16type TestLink = "hello" :> "hi" :> Get Bool
17type TestLink = "hello" :> "hi" :> Get Bool
17type TestLink2 = "greet" :> Post Bool
18type TestLink2 = "greet" :> Post Bool
19+type TestLink3 = "parent" :> "child" :> Get String
18
20
19type BadTestLink = "hallo" :> "hi" :> Get Bool
21type BadTestLink = "hallo" :> "hi" :> Get Bool
20type BadTestLink2 = "greet" :> Get Bool
22type BadTestLink2 = "greet" :> Get Bool
23+type BadTestLink3 = "parent" :> "child" :> MatrixFlag "male" :> Get String
21
24
22type NotALink = "hello" :> Capture "x" Bool :> Get Bool
25type NotALink = "hello" :> Capture "x" Bool :> Get Bool
23type NotALink2 = "hello" :> ReqBody 'True :> Get Bool
26type NotALink2 = "hello" :> ReqBody 'True :> Get Bool
@@ -37,15 +40,18 @@ isElem = describe "IsElem" $ do
37 it "is True when the first argument is an url within the second" $ do
40 it "is True when the first argument is an url within the second" $ do
38 reflected (Proxy::Proxy (IsElem TestLink TestApi)) ~> True
41 reflected (Proxy::Proxy (IsElem TestLink TestApi)) ~> True
39 reflected (Proxy::Proxy (IsElem TestLink2 TestApi)) ~> True
42 reflected (Proxy::Proxy (IsElem TestLink2 TestApi)) ~> True
43+ reflected (Proxy::Proxy (IsElem TestLink3 TestApi)) ~> True
40 it "is False when the first argument is not an url within the second" $ do
44 it "is False when the first argument is not an url within the second" $ do
41 reflected (Proxy::Proxy (IsElem BadTestLink TestApi)) ~> False
45 reflected (Proxy::Proxy (IsElem BadTestLink TestApi)) ~> False
42 reflected (Proxy::Proxy (IsElem BadTestLink2 TestApi)) ~> False
46 reflected (Proxy::Proxy (IsElem BadTestLink2 TestApi)) ~> False
47+ reflected (Proxy::Proxy (IsElem BadTestLink3 TestApi)) ~> False
43
48
44isLink :: Spec
49isLink :: Spec
45isLink = describe "IsLink" $ do
50isLink = describe "IsLink" $ do
46 it "is True when all Subs are paths and the last is a method" $ do
51 it "is True when all Subs are paths and the last is a method" $ do
47 reflected (Proxy::Proxy (IsLink TestLink)) ~> True
52 reflected (Proxy::Proxy (IsLink TestLink)) ~> True
48 reflected (Proxy::Proxy (IsLink TestLink2)) ~> True
53 reflected (Proxy::Proxy (IsLink TestLink2)) ~> True
54+ reflected (Proxy::Proxy (IsLink TestLink3)) ~> True
49 it "is False of anything with captures" $ do
55 it "is False of anything with captures" $ do
50 reflected (Proxy::Proxy (IsLink NotALink)) ~> False
56 reflected (Proxy::Proxy (IsLink NotALink)) ~> False
51 reflected (Proxy::Proxy (IsLink NotALink2)) ~> False
57 reflected (Proxy::Proxy (IsLink NotALink2)) ~> False