module Control.Monad.Apiary.Filter (
method
, http09, http10, http11
, root
, capture
, (??)
, (=:), (=!:), (=?:), (=?!:), (=*:), (=+:)
, switchQuery
, eqHeader
, header
, accept
, ssl
, HasDesc(..)
, QueryKey(..)
, query
, Control.Monad.Apiary.Filter.httpVersion
, Capture.path
, Capture.endPath
, Capture.fetch
, Capture.restPath
, Capture.anyPath
, function, function', function_, focus
, Doc(..)
) where
import qualified Network.Wai as Wai
import Network.Wai.Parse (parseContentType, parseHttpAccept)
import qualified Network.HTTP.Types as HTTP
import Control.Applicative((<$>))
import Control.Monad(mzero)
import Control.Monad.Trans(MonadIO)
import Control.Monad.Apiary.Action.Internal
(getParams, getQueryParams, getRequestBody, getRequest, ContentType, contentType)
import Control.Monad.Apiary.Filter.Internal
( function, function', function_
, Doc(DocMethod, DocPrecondition, DocRoot, DocQuery, DocAccept))
import Control.Monad.Apiary.Filter.Internal.Capture.TH(capture)
import Control.Monad.Apiary.Internal(ApiaryT, focus', focus, PathElem(RootPath))
import qualified Control.Monad.Apiary.Filter.Internal.Capture as Capture
import Text.Blaze.Html(Html, toHtml)
import qualified Data.ByteString.Char8 as SC
import qualified Data.Text as T
import qualified Data.CaseInsensitive as CI
import Data.Monoid((<>))
import Data.Apiary.Compat(KnownSymbol, Symbol, symbolVal, Proxy(..), SProxy(..))
import Data.Apiary.Dict(NotMember, Elem((:=)))
import qualified Data.Apiary.Dict as Dict
import Data.Apiary.Param
( ReqParam, StrategyRep(..), QueryRep(NoValue)
, Strategy(..), reqParamRep, reqParams
, pFirst, pOne, pOption, pOptional, pMany, pSome
)
import Data.Apiary.Method(Method)
method :: Monad actM => Method -> ApiaryT exts prms actM m () -> ApiaryT exts prms actM m ()
method m = focus' (DocMethod m) (Just m) id getParams
ssl :: Monad actM => ApiaryT exts prms actM m () -> ApiaryT exts prms actM m ()
ssl = function_ (DocPrecondition "SSL required") Wai.isSecure
httpVersion :: Monad actM => HTTP.HttpVersion -> Html -> ApiaryT exts prms actM m () -> ApiaryT exts prms actM m ()
httpVersion v h = function_ (DocPrecondition h) $ (v ==) . Wai.httpVersion
http09 :: Monad actM => ApiaryT exts prms actM m () -> ApiaryT exts prms actM m ()
http09 = Control.Monad.Apiary.Filter.httpVersion HTTP.http09 "HTTP/0.9 only"
http10 :: Monad actM => ApiaryT exts prms actM m () -> ApiaryT exts prms actM m ()
http10 = Control.Monad.Apiary.Filter.httpVersion HTTP.http10 "HTTP/1.0 only"
http11 :: Monad actM => ApiaryT exts prms actM m () -> ApiaryT exts prms actM m ()
http11 = Control.Monad.Apiary.Filter.httpVersion HTTP.http11 "HTTP/1.1 only"
root :: (Monad m, Monad actM) => ApiaryT exts prms actM m () -> ApiaryT exts prms actM m ()
root = focus' DocRoot Nothing (RootPath:) getParams
newtype QueryKey (key :: Symbol) = QueryKey { queryKeyDesc :: Maybe Html }
(??) :: proxy key -> Html -> QueryKey key
_ ?? d = QueryKey (Just d)
class HasDesc (a :: Symbol -> *) where
queryDesc :: a key -> Maybe Html
instance HasDesc QueryKey where
queryDesc = queryKeyDesc
instance HasDesc Proxy where
queryDesc = const Nothing
instance HasDesc SProxy where
queryDesc = const Nothing
query :: forall query strategy k v exts prms actM m. (NotMember k prms, MonadIO actM, KnownSymbol k, ReqParam v, HasDesc query, Strategy strategy)
=> query k -> strategy v -> ApiaryT exts (SNext strategy k v prms) actM m () -> ApiaryT exts prms actM m ()
query k w = focus (DocQuery (T.pack $ symbolVal k) (strategyRep w) (reqParamRep (Proxy :: Proxy v)) (queryDesc k)) $ do
qs <- getQueryParams
(ps,fs) <- getRequestBody
let as = map snd . filter ((SC.pack (symbolVal k) ==) . fst) $ reqParams (Proxy :: Proxy v) qs ps fs
maybe mzero return . strategy w k as =<< getParams
(=:) :: (HasDesc query, MonadIO actM, ReqParam v, KnownSymbol k, NotMember k prms)
=> query k -> proxy v -> ApiaryT exts (k := v ': prms) actM m () -> ApiaryT exts prms actM m ()
k =: v = query k (pFirst v)
(=!:) :: (HasDesc query, MonadIO actM, ReqParam v, KnownSymbol k, NotMember k prms)
=> query k -> proxy v -> ApiaryT exts (k := v ': prms) actM m () -> ApiaryT exts prms actM m ()
k =!: t = query k (pOne t)
(=?:) :: (HasDesc query, MonadIO actM, ReqParam v, KnownSymbol k, NotMember k prms)
=> query k -> proxy v
-> ApiaryT exts (k := Maybe v ': prms) actM m () -> ApiaryT exts prms actM m ()
k =?: t = query k (pOption t)
(=?!:) :: forall query k v exts prms actM m. (HasDesc query, MonadIO actM, Show v, ReqParam v, KnownSymbol k, NotMember k prms)
=> query k -> v
-> ApiaryT exts (k := v ': prms) actM m () -> ApiaryT exts prms actM m ()
k =?!: v = query k (pOptional v)
(=*:) :: (HasDesc query, MonadIO actM, ReqParam v, KnownSymbol k, NotMember k prms)
=> query k -> proxy v
-> ApiaryT exts (k := [v] ': prms) actM m () -> ApiaryT exts prms actM m ()
k =*: t = query k (pMany t)
(=+:) :: (HasDesc query, MonadIO actM, ReqParam v, KnownSymbol k, NotMember k prms)
=> query k -> proxy v
-> ApiaryT exts (k := [v] ': prms) actM m () -> ApiaryT exts prms actM m ()
k =+: t = query k (pSome t)
switchQuery :: (HasDesc proxy, MonadIO actM, KnownSymbol k, NotMember k prms)
=> proxy k -> ApiaryT exts (k := Bool ': prms) actM m () -> ApiaryT exts prms actM m ()
switchQuery k = focus (DocQuery (T.pack $ symbolVal k) (StrategyRep "switch") NoValue (queryDesc k)) $ do
qs <- getQueryParams
(ps,fs) <- getRequestBody
let n = maybe False id . fmap (maybe True id) . lookup (SC.pack $ symbolVal k) $ reqParams (Proxy :: Proxy Bool) qs ps fs
Dict.insert k n <$> getParams
header :: (KnownSymbol k, Monad actM, NotMember k prms)
=> proxy k -> ApiaryT exts (k := SC.ByteString ': prms) actM m () -> ApiaryT exts prms actM m ()
header k = focus' (DocPrecondition $ "has header: " <> toHtml (symbolVal k)) Nothing id $ do
n <- maybe mzero return . lookup (CI.mk . SC.pack $ symbolVal k) . Wai.requestHeaders =<< getRequest
Dict.insert k n <$> getParams
eqHeader :: (KnownSymbol k, Monad actM)
=> proxy k -> SC.ByteString -> ApiaryT exts prms actM m () -> ApiaryT exts prms actM m ()
eqHeader k v = focus' (DocPrecondition $ "header: " <> toHtml (symbolVal k) <> " = " <> toHtml (show v)) Nothing id $ do
v' <- maybe mzero return . lookup (CI.mk . SC.pack $ symbolVal k) . Wai.requestHeaders =<< getRequest
if v == v' then getParams else mzero
accept :: Monad actM => ContentType -> ApiaryT exts prms actM m () -> ApiaryT exts prms actM m ()
accept ect = focus (DocAccept ect) $
(lookup "Accept" . Wai.requestHeaders <$> getRequest) >>= \case
Nothing -> mzero
Just ac ->
let ex@(et, _) = parseContentType ect
accepts = map parseContentType (parseHttpAccept ac)
in case filter (matchContentType ex) accepts of
[] -> mzero
(_,p):_ -> contentType (prettyContentType et p) >> getParams
matchContentType :: (SC.ByteString, [(SC.ByteString, SC.ByteString)])
-> (SC.ByteString, [(SC.ByteString, SC.ByteString)])
-> Bool
matchContentType (ct, ep) (acc, ip) = case SC.break (== '/') acc of
("*", "/*") -> prmCheck
(a, "/*") -> a == SC.takeWhile (/= '/') ct && prmCheck
_ -> acc == ct && prmCheck
where
prmCheck = all (\(k,v) -> Just v == lookup k ip) ep
prettyContentType :: SC.ByteString -> [(SC.ByteString, SC.ByteString)] -> SC.ByteString
prettyContentType ct prms =
let pprms = SC.concat $ concatMap (\(k,v) -> [";", k, "=", v]) prms
in ct `SC.append` pprms