@@ -3,12 +3,13 @@
3{-# LANGUAGE TypeOperators #-}
3{-# LANGUAGE TypeOperators #-}
4{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE OverloadedStrings #-}
6{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
7module Servant.API.QueryParam where
8module Servant.API.QueryParam where
9import Data.Proxy
11import Data.Proxy
10import Data.String.Conversions
12import Data.String.Conversions
12import GHC.TypeLits
13import GHC.TypeLits
13import Network.HTTP.Types
14import Network.HTTP.Types
14import Network.Wai
15import Network.Wai
@@ -18,9 +19,9 @@ import Servant.Docs
18import Servant.Server
19import Servant.Server
19import Servant.Utils.Text
20import Servant.Utils.Text
21−-- * Query String parameter lookup
22+-- * Single query string parameter lookup
23−-- | Your must implement:
24+-- | You must implement:
25-- - a @'FromText' a@ instance for serving
26-- - a @'FromText' a@ instance for serving
26-- - a @'ToText' a@ instance for (client-side) querying
27-- - a @'ToText' a@ instance for (client-side) querying
@@ -57,7 +58,7 @@ instance (KnownSymbol sym, ToText a, HasClient sublayout)
57 clientWithRoute (Proxy :: Proxy sublayout) $
58 clientWithRoute (Proxy :: Proxy sublayout) $
58 appendToQueryString pname mparamText req
59 appendToQueryString pname mparamText req
60− where pname = pack pname'
61+ where pname = cs pname'
61 pname' = symbolVal (Proxy :: Proxy sym)
62 pname' = symbolVal (Proxy :: Proxy sym)
62 mparamText = fmap toText mparam
63 mparamText = fmap toText mparam
@@ -70,3 +71,74 @@ instance (KnownSymbol sym, ToParam (QueryParam sym a), HasDocs sublayout)
70 where sublayoutP = Proxy :: Proxy sublayout
71 where sublayoutP = Proxy :: Proxy sublayout
71 paramP = Proxy :: Proxy (QueryParam sym a)
72 paramP = Proxy :: Proxy (QueryParam sym a)
72 action' = over params (|> toParam paramP) action
73 action' = over params (|> toParam paramP) action
75+-- | Retrieve a list from the query string.
76+data QueryParams sym a
78+instance (KnownSymbol sym, FromText a, HasServer sublayout)
79+ => HasServer (QueryParams sym a :> sublayout) where
81+ type Server (QueryParams sym a :> sublayout) =
82+ [a] -> Server sublayout
84+ route Proxy subserver request respond = do
85+ let querytext = parseQueryText $ rawQueryString request
86+ -- if sym is "foo", we look for query string parameters
87+ -- named "foo" or "foo[]" and call fromText on the
88+ -- corresponding values
89+ parameters = filter looksLikeParam querytext
90+ values = catMaybes $ map (convert . snd) parameters
92+ route (Proxy :: Proxy sublayout) (subserver values) request respond
94+ where paramname = cs $ symbolVal (Proxy :: Proxy sym)
95+ looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
96+ convert Nothing = Nothing
97+ convert (Just v) = fromText v
99+instance (KnownSymbol sym, ToText a, HasClient sublayout)
100+ => HasClient (QueryParams sym a :> sublayout) where
102+ type Client (QueryParams sym a :> sublayout) =
103+ [a] -> Client sublayout
105+ clientWithRoute Proxy req paramlist =
106+ clientWithRoute (Proxy :: Proxy sublayout) $
107+ foldr (appendToQueryString pname) req paramlist'
109+ where pname = cs pname'
110+ pname' = symbolVal (Proxy :: Proxy sym)
111+ paramlist' = map (Just . toText) paramlist
113+-- | Retrieve a value-less boolean from the query string.
116+instance (KnownSymbol sym, HasServer sublayout)
117+ => HasServer (QueryFlag sym :> sublayout) where
119+ type Server (QueryFlag sym :> sublayout) =
120+ Bool -> Server sublayout
122+ route Proxy subserver request respond = do
123+ let querytext = parseQueryText $ rawQueryString request
124+ param = case lookup paramname querytext of
125+ Just Nothing -> True -- param is there, with no value
126+ _ -> False -- param not in the query string or with a value
128+ route (Proxy :: Proxy sublayout) (subserver param) request respond
130+ where paramname = cs $ symbolVal (Proxy :: Proxy sym)
132+instance (KnownSymbol sym, HasClient sublayout)
133+ => HasClient (QueryFlag sym :> sublayout) where
135+ type Client (QueryFlag sym :> sublayout) =
136+ Bool -> Client sublayout
138+ clientWithRoute Proxy req flag =
139+ clientWithRoute (Proxy :: Proxy sublayout) $
141+ then appendToQueryString paramname Nothing req
144+ where paramname = cs $ symbolVal (Proxy :: Proxy sym)