haddocks for all 3 forms of query params (phew)
This commit is contained in:
parent
c47f4ae002
commit
77d52083f1
2 changed files with 153 additions and 10 deletions
|
@ -94,7 +94,7 @@ instance (KnownSymbol capture, ToText a, HasClient sublayout)
|
||||||
where p = unpack (toText val)
|
where p = unpack (toText val)
|
||||||
|
|
||||||
-- | @"books" :> 'Capture' "isbn" Text@ will appear as
|
-- | @"books" :> 'Capture' "isbn" Text@ will appear as
|
||||||
-- @/books/:isbn@ in the docs
|
-- @/books/:isbn@ in the docs.
|
||||||
instance (KnownSymbol sym, ToCapture (Capture sym a), HasDocs sublayout)
|
instance (KnownSymbol sym, ToCapture (Capture sym a), HasDocs sublayout)
|
||||||
=> HasDocs (Capture sym a :> sublayout) where
|
=> HasDocs (Capture sym a :> sublayout) where
|
||||||
|
|
||||||
|
|
|
@ -21,15 +21,36 @@ import Servant.Common.Text
|
||||||
import Servant.Docs
|
import Servant.Docs
|
||||||
import Servant.Server
|
import Servant.Server
|
||||||
|
|
||||||
-- * Single query string parameter lookup
|
-- | Lookup the value associated to the @sym@ query string parameter
|
||||||
|
-- and try to extract it as a value of type @a@.
|
||||||
-- | You must implement:
|
|
||||||
--
|
--
|
||||||
-- - a @'FromText' a@ instance for serving
|
-- Example:
|
||||||
-- - a @'ToText' a@ instance for (client-side) querying
|
--
|
||||||
-- - a @'ToParam' ('QueryParam' sym a)@ instance for automatic documentation generation
|
-- > -- /books?author=<author name>
|
||||||
|
-- > type MyApi = "books" :> QueryParam "author" Text :> Get [Book]
|
||||||
data QueryParam sym a
|
data QueryParam sym a
|
||||||
|
|
||||||
|
-- | If you use @'QueryParam' "author" Text@ in one of the endpoints for your API,
|
||||||
|
-- this automatically requires your server-side handler to be a function
|
||||||
|
-- that takes an argument of type @'Maybe' 'Text'@.
|
||||||
|
--
|
||||||
|
-- This lets servant worry about looking it up in the query string
|
||||||
|
-- and turning it into a value of the type you specify, enclosed
|
||||||
|
-- in 'Maybe', because it may not be there and servant would then
|
||||||
|
-- hand you 'Nothing'.
|
||||||
|
--
|
||||||
|
-- You can control how it'll be converted from 'Text' to your type
|
||||||
|
-- by simply providing an instance of 'FromText' for your type.
|
||||||
|
--
|
||||||
|
-- Example:
|
||||||
|
--
|
||||||
|
-- > type MyApi = "books" :> QueryParam "author" Text :> Get [Book]
|
||||||
|
-- >
|
||||||
|
-- > server :: Server MyApi
|
||||||
|
-- > server = getBooksBy
|
||||||
|
-- > where getBooksBy :: Maybe Text -> EitherT (Int, String) IO [Book]
|
||||||
|
-- > getBooksBy Nothing = ...return all books...
|
||||||
|
-- > getBooksBy (Just author) = ...return books by the given author...
|
||||||
instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
||||||
=> HasServer (QueryParam sym a :> sublayout) where
|
=> HasServer (QueryParam sym a :> sublayout) where
|
||||||
|
|
||||||
|
@ -49,6 +70,31 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
||||||
|
|
||||||
where paramname = cs $ symbolVal (Proxy :: Proxy sym)
|
where paramname = cs $ symbolVal (Proxy :: Proxy sym)
|
||||||
|
|
||||||
|
-- | If you use a 'QueryParam' in one of your endpoints in your API,
|
||||||
|
-- the corresponding querying function will automatically take
|
||||||
|
-- an additional argument of the type specified by your 'QueryParam',
|
||||||
|
-- enclosed in Maybe.
|
||||||
|
--
|
||||||
|
-- If you give Nothing, nothing will be added to the query string.
|
||||||
|
--
|
||||||
|
-- If you give a non-'Nothing' value, this function will take care
|
||||||
|
-- of inserting a textual representation of this value in the query string.
|
||||||
|
--
|
||||||
|
-- You can control how values for your type are turned into
|
||||||
|
-- text by specifying a 'ToText' instance for your type.
|
||||||
|
--
|
||||||
|
-- Example:
|
||||||
|
--
|
||||||
|
-- > type MyApi = "books" :> QueryParam "author" Text :> Get [Book]
|
||||||
|
-- >
|
||||||
|
-- > myApi :: Proxy MyApi
|
||||||
|
-- > myApi = Proxy
|
||||||
|
-- >
|
||||||
|
-- > getBooksBy :: Maybe Text -> BaseUrl -> EitherT String IO [Book]
|
||||||
|
-- > getBooksBy = client myApi
|
||||||
|
-- > -- then you can just use "getBooksBy" to query that endpoint.
|
||||||
|
-- > -- 'getBooksBy Nothing' for all books
|
||||||
|
-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
|
||||||
instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
||||||
=> HasClient (QueryParam sym a :> sublayout) where
|
=> HasClient (QueryParam sym a :> sublayout) where
|
||||||
|
|
||||||
|
@ -74,9 +120,38 @@ instance (KnownSymbol sym, ToParam (QueryParam sym a), HasDocs sublayout)
|
||||||
paramP = Proxy :: Proxy (QueryParam sym a)
|
paramP = Proxy :: Proxy (QueryParam sym a)
|
||||||
action' = over params (|> toParam paramP) action
|
action' = over params (|> toParam paramP) action
|
||||||
|
|
||||||
-- | Retrieve a list from the query string.
|
-- | Lookup the values associated to the @sym@ query string parameter
|
||||||
|
-- and try to extract it as a value of type @[a]@. This is typically
|
||||||
|
-- meant to support query string parameters of the form
|
||||||
|
-- @param[]=val1¶m[]=val2@ and so on. Note that servant doesn't actually
|
||||||
|
-- require the @[]@s and will fetch the values just fine with
|
||||||
|
-- @param=val1¶m=val2@, too.
|
||||||
|
--
|
||||||
|
-- Example:
|
||||||
|
--
|
||||||
|
-- > -- /books?authors[]=<author1>&authors[]=<author2>&...
|
||||||
|
-- > type MyApi = "books" :> QueryParams "authors" Text :> Get [Book]
|
||||||
data QueryParams sym a
|
data QueryParams sym a
|
||||||
|
|
||||||
|
-- | If you use @'QueryParams' "authors" Text@ in one of the endpoints for your API,
|
||||||
|
-- this automatically requires your server-side handler to be a function
|
||||||
|
-- that takes an argument of type @['Text']@.
|
||||||
|
--
|
||||||
|
-- This lets servant worry about looking up 0 or more values in the query string
|
||||||
|
-- associated to @authors@ and turning each of them into a value of
|
||||||
|
-- the type you specify.
|
||||||
|
--
|
||||||
|
-- You can control how the individual values are converted from 'Text' to your type
|
||||||
|
-- by simply providing an instance of 'FromText' for your type.
|
||||||
|
--
|
||||||
|
-- Example:
|
||||||
|
--
|
||||||
|
-- > type MyApi = "books" :> QueryParams "authors" Text :> Get [Book]
|
||||||
|
-- >
|
||||||
|
-- > server :: Server MyApi
|
||||||
|
-- > server = getBooksBy
|
||||||
|
-- > where getBooksBy :: [Text] -> EitherT (Int, String) IO [Book]
|
||||||
|
-- > getBooksBy authors = ...return all books by these authors...
|
||||||
instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
||||||
=> HasServer (QueryParams sym a :> sublayout) where
|
=> HasServer (QueryParams sym a :> sublayout) where
|
||||||
|
|
||||||
|
@ -98,6 +173,33 @@ instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
||||||
convert Nothing = Nothing
|
convert Nothing = Nothing
|
||||||
convert (Just v) = fromText v
|
convert (Just v) = fromText v
|
||||||
|
|
||||||
|
-- | If you use a 'QueryParams' in one of your endpoints in your API,
|
||||||
|
-- the corresponding querying function will automatically take
|
||||||
|
-- an additional argument, a list of values of the type specified
|
||||||
|
-- by your 'QueryParams'.
|
||||||
|
--
|
||||||
|
-- If you give an empty list, nothing will be added to the query string.
|
||||||
|
--
|
||||||
|
-- Otherwise, this function will take care
|
||||||
|
-- of inserting a textual representation of your values in the query string,
|
||||||
|
-- under the same query string parameter name.
|
||||||
|
--
|
||||||
|
-- You can control how values for your type are turned into
|
||||||
|
-- text by specifying a 'ToText' instance for your type.
|
||||||
|
--
|
||||||
|
-- Example:
|
||||||
|
--
|
||||||
|
-- > type MyApi = "books" :> QueryParams "authors" Text :> Get [Book]
|
||||||
|
-- >
|
||||||
|
-- > myApi :: Proxy MyApi
|
||||||
|
-- > myApi = Proxy
|
||||||
|
-- >
|
||||||
|
-- > getBooksBy :: [Text] -> BaseUrl -> EitherT String IO [Book]
|
||||||
|
-- > getBooksBy = client myApi
|
||||||
|
-- > -- then you can just use "getBooksBy" to query that endpoint.
|
||||||
|
-- > -- 'getBooksBy []' for all books
|
||||||
|
-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
|
||||||
|
-- > -- to get all books by Asimov and Heinlein
|
||||||
instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
||||||
=> HasClient (QueryParams sym a :> sublayout) where
|
=> HasClient (QueryParams sym a :> sublayout) where
|
||||||
|
|
||||||
|
@ -122,9 +224,29 @@ instance (KnownSymbol sym, ToParam (QueryParams sym a), HasDocs sublayout)
|
||||||
paramP = Proxy :: Proxy (QueryParams sym a)
|
paramP = Proxy :: Proxy (QueryParams sym a)
|
||||||
action' = over params (|> toParam paramP) action
|
action' = over params (|> toParam paramP) action
|
||||||
|
|
||||||
-- | Retrieve a value-less boolean from the query string.
|
-- | Lookup a potentially value-less query string parameter
|
||||||
data QueryFlag a
|
-- with boolean semantics. If the param @sym@ is there without any value,
|
||||||
|
-- or if it's there with value "true" or "1", it's interpreted as 'True'.
|
||||||
|
-- Otherwise, it's interpreted as 'False'.
|
||||||
|
--
|
||||||
|
-- Example:
|
||||||
|
--
|
||||||
|
-- > -- /books?published
|
||||||
|
-- > type MyApi = "books" :> QueryFlag "published" :> Get [Book]
|
||||||
|
data QueryFlag sym
|
||||||
|
|
||||||
|
-- | If you use @'QueryFlag' "published"@ in one of the endpoints for your API,
|
||||||
|
-- this automatically requires your server-side handler to be a function
|
||||||
|
-- that takes an argument of type 'Bool'.
|
||||||
|
--
|
||||||
|
-- Example:
|
||||||
|
--
|
||||||
|
-- > type MyApi = "books" :> QueryFlag "published" :> Get [Book]
|
||||||
|
-- >
|
||||||
|
-- > server :: Server MyApi
|
||||||
|
-- > server = getBooks
|
||||||
|
-- > where getBooks :: Bool -> EitherT (Int, String) IO [Book]
|
||||||
|
-- > getBooks onlyPublished = ...return all books, or only the ones that are already published, depending on the argument...
|
||||||
instance (KnownSymbol sym, HasServer sublayout)
|
instance (KnownSymbol sym, HasServer sublayout)
|
||||||
=> HasServer (QueryFlag sym :> sublayout) where
|
=> HasServer (QueryFlag sym :> sublayout) where
|
||||||
|
|
||||||
|
@ -144,6 +266,27 @@ instance (KnownSymbol sym, HasServer sublayout)
|
||||||
examine v | v == "true" || v == "1" || v == "" = True
|
examine v | v == "true" || v == "1" || v == "" = True
|
||||||
| otherwise = False
|
| otherwise = False
|
||||||
|
|
||||||
|
-- | If you use a 'QueryFlag' in one of your endpoints in your API,
|
||||||
|
-- the corresponding querying function will automatically take
|
||||||
|
-- an additional 'Bool' argument.
|
||||||
|
--
|
||||||
|
-- If you give 'False', nothing will be added to the query string.
|
||||||
|
--
|
||||||
|
-- Otherwise, this function will insert a value-less query string
|
||||||
|
-- parameter under the name associated to your 'QueryFlag'.
|
||||||
|
--
|
||||||
|
-- Example:
|
||||||
|
--
|
||||||
|
-- > type MyApi = "books" :> QueryFlag "published" :> Get [Book]
|
||||||
|
-- >
|
||||||
|
-- > myApi :: Proxy MyApi
|
||||||
|
-- > myApi = Proxy
|
||||||
|
-- >
|
||||||
|
-- > getBooks :: Bool -> BaseUrl -> EitherT String IO [Book]
|
||||||
|
-- > getBooks = client myApi
|
||||||
|
-- > -- then you can just use "getBooks" to query that endpoint.
|
||||||
|
-- > -- 'getBooksBy False' for all books
|
||||||
|
-- > -- 'getBooksBy True' to only get _already published_ books
|
||||||
instance (KnownSymbol sym, HasClient sublayout)
|
instance (KnownSymbol sym, HasClient sublayout)
|
||||||
=> HasClient (QueryFlag sym :> sublayout) where
|
=> HasClient (QueryFlag sym :> sublayout) where
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue