2014-10-25 01:27:39 +02:00
|
|
|
{-# LANGUAGE PolyKinds #-}
|
|
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
|
|
{-# LANGUAGE TypeOperators #-}
|
2014-10-28 09:04:27 +01:00
|
|
|
{-# LANGUAGE FlexibleContexts #-}
|
2014-10-25 01:27:39 +02:00
|
|
|
{-# LANGUAGE FlexibleInstances #-}
|
|
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
2014-10-28 15:06:47 +01:00
|
|
|
module Servant.API.QueryParam where
|
2014-10-25 01:27:39 +02:00
|
|
|
|
|
|
|
import Data.Proxy
|
|
|
|
import Data.String.Conversions
|
|
|
|
import Data.Text
|
|
|
|
import GHC.TypeLits
|
|
|
|
import Network.HTTP.Types
|
|
|
|
import Network.Wai
|
|
|
|
import Servant.API.Sub
|
|
|
|
import Servant.Client
|
2014-10-28 09:04:27 +01:00
|
|
|
import Servant.Docs
|
2014-10-25 01:27:39 +02:00
|
|
|
import Servant.Server
|
2014-10-28 12:34:41 +01:00
|
|
|
import Servant.Utils.Text
|
2014-10-25 01:27:39 +02:00
|
|
|
|
2014-10-28 15:06:47 +01:00
|
|
|
-- * Query String parameter lookup
|
|
|
|
|
|
|
|
-- | Your must implement:
|
|
|
|
--
|
|
|
|
-- - a @'FromText' a@ instance for serving
|
|
|
|
-- - a @'ToText' a@ instance for (client-side) querying
|
|
|
|
-- - a @'ToParam' ('QueryParam' sym a)@ instance for automatic documentation generation
|
|
|
|
data QueryParam sym a
|
2014-10-25 01:27:39 +02:00
|
|
|
|
|
|
|
instance (KnownSymbol sym, FromText a, HasServer sublayout)
|
2014-10-28 15:06:47 +01:00
|
|
|
=> HasServer (QueryParam sym a :> sublayout) where
|
2014-10-25 01:27:39 +02:00
|
|
|
|
2014-10-28 15:06:47 +01:00
|
|
|
type Server (QueryParam sym a :> sublayout) =
|
2014-10-25 01:27:39 +02:00
|
|
|
Maybe a -> Server sublayout
|
|
|
|
|
2014-10-28 10:42:49 +01:00
|
|
|
route Proxy subserver request respond = do
|
2014-10-25 01:27:39 +02:00
|
|
|
let querytext = parseQueryText $ rawQueryString request
|
|
|
|
param =
|
2014-10-28 09:04:27 +01:00
|
|
|
case lookup paramname querytext of
|
2014-10-25 01:27:39 +02:00
|
|
|
Nothing -> Nothing -- param absent from the query string
|
|
|
|
Just Nothing -> Nothing -- param present with no value -> Nothing
|
|
|
|
Just (Just v) -> fromText v -- if present, we try to convert to
|
|
|
|
-- the right type
|
|
|
|
|
2014-10-28 10:42:49 +01:00
|
|
|
route (Proxy :: Proxy sublayout) (subserver param) request respond
|
2014-10-25 01:27:39 +02:00
|
|
|
|
2014-10-28 09:04:27 +01:00
|
|
|
where paramname = cs $ symbolVal (Proxy :: Proxy sym)
|
2014-10-25 01:27:39 +02:00
|
|
|
|
|
|
|
instance (KnownSymbol sym, ToText a, HasClient sublayout)
|
2014-10-28 15:06:47 +01:00
|
|
|
=> HasClient (QueryParam sym a :> sublayout) where
|
2014-10-25 01:27:39 +02:00
|
|
|
|
2014-10-28 15:06:47 +01:00
|
|
|
type Client (QueryParam sym a :> sublayout) =
|
2014-10-25 01:27:39 +02:00
|
|
|
Maybe a -> Client sublayout
|
|
|
|
|
|
|
|
-- if mparam = Nothing, we don't add it to the query string
|
|
|
|
clientWithRoute Proxy req mparam =
|
|
|
|
clientWithRoute (Proxy :: Proxy sublayout) $
|
|
|
|
appendToQueryString pname mparamText req
|
|
|
|
|
|
|
|
where pname = pack pname'
|
|
|
|
pname' = symbolVal (Proxy :: Proxy sym)
|
|
|
|
mparamText = fmap toText mparam
|
2014-10-28 09:04:27 +01:00
|
|
|
|
2014-10-28 15:06:47 +01:00
|
|
|
instance (KnownSymbol sym, ToParam (QueryParam sym a), HasDocs sublayout)
|
|
|
|
=> HasDocs (QueryParam sym a :> sublayout) where
|
2014-10-28 09:04:27 +01:00
|
|
|
|
|
|
|
docsFor Proxy (endpoint, action) =
|
|
|
|
docsFor sublayoutP (endpoint, action')
|
|
|
|
|
|
|
|
where sublayoutP = Proxy :: Proxy sublayout
|
2014-10-28 15:06:47 +01:00
|
|
|
paramP = Proxy :: Proxy (QueryParam sym a)
|
2014-10-28 09:04:27 +01:00
|
|
|
action' = over params (|> toParam paramP) action
|