apiary 0.8.0.0 → 0.9.0.0
raw patch · 8 files changed
+131/−34 lines, 8 filesdep +wai-extradep −transformersdep −warpdep ~waidep ~wai-testPVP ok
version bump matches the API change (PVP)
Dependencies added: wai-extra
Dependencies removed: transformers, warp
Dependency ranges changed: wai, wai-test
API changes (from Hackage documentation)
- Data.Apiary.Param: instance Path Bool
- Data.Apiary.Param: instance Path ByteString
- Data.Apiary.Param: instance Path Char
- Data.Apiary.Param: instance Path Double
- Data.Apiary.Param: instance Path Float
- Data.Apiary.Param: instance Path Int
- Data.Apiary.Param: instance Path Int16
- Data.Apiary.Param: instance Path Int32
- Data.Apiary.Param: instance Path Int64
- Data.Apiary.Param: instance Path Int8
- Data.Apiary.Param: instance Path Integer
- Data.Apiary.Param: instance Path String
- Data.Apiary.Param: instance Path Text
- Data.Apiary.Param: instance Path Word
- Data.Apiary.Param: instance Path Word16
- Data.Apiary.Param: instance Path Word32
- Data.Apiary.Param: instance Path Word64
- Data.Apiary.Param: instance Path Word8
- Data.Apiary.Param: instance Query ()
- Data.Apiary.Param: instance Query Bool
- Data.Apiary.Param: instance Query ByteString
- Data.Apiary.Param: instance Query Double
- Data.Apiary.Param: instance Query Float
- Data.Apiary.Param: instance Query Int
- Data.Apiary.Param: instance Query Int16
- Data.Apiary.Param: instance Query Int32
- Data.Apiary.Param: instance Query Int64
- Data.Apiary.Param: instance Query Int8
- Data.Apiary.Param: instance Query Integer
- Data.Apiary.Param: instance Query String
- Data.Apiary.Param: instance Query Text
- Data.Apiary.Param: instance Query Word
- Data.Apiary.Param: instance Query Word16
- Data.Apiary.Param: instance Query Word32
- Data.Apiary.Param: instance Query Word64
- Data.Apiary.Param: instance Query Word8
- Data.Apiary.Param: instance Query a => Query (Maybe a)
+ Control.Monad.Apiary.Action: getReqFiles :: MonadIO m => ActionT m [File ByteString]
+ Control.Monad.Apiary.Action: getReqParams :: MonadIO m => ActionT m [Param]
+ Control.Monad.Apiary.Action: stream :: Monad m => StreamingBody -> ActionT m ()
+ Control.Monad.Apiary.Action: type StreamingBody = Source IO (Flush Builder)
+ Data.Apiary.Param: class ReqParam a
+ Data.Apiary.Param: instance [overlap ok] Path Bool
+ Data.Apiary.Param: instance [overlap ok] Path ByteString
+ Data.Apiary.Param: instance [overlap ok] Path Char
+ Data.Apiary.Param: instance [overlap ok] Path Double
+ Data.Apiary.Param: instance [overlap ok] Path Float
+ Data.Apiary.Param: instance [overlap ok] Path Int
+ Data.Apiary.Param: instance [overlap ok] Path Int16
+ Data.Apiary.Param: instance [overlap ok] Path Int32
+ Data.Apiary.Param: instance [overlap ok] Path Int64
+ Data.Apiary.Param: instance [overlap ok] Path Int8
+ Data.Apiary.Param: instance [overlap ok] Path Integer
+ Data.Apiary.Param: instance [overlap ok] Path String
+ Data.Apiary.Param: instance [overlap ok] Path Text
+ Data.Apiary.Param: instance [overlap ok] Path Word
+ Data.Apiary.Param: instance [overlap ok] Path Word16
+ Data.Apiary.Param: instance [overlap ok] Path Word32
+ Data.Apiary.Param: instance [overlap ok] Path Word64
+ Data.Apiary.Param: instance [overlap ok] Path Word8
+ Data.Apiary.Param: instance [overlap ok] Query ()
+ Data.Apiary.Param: instance [overlap ok] Query Bool
+ Data.Apiary.Param: instance [overlap ok] Query ByteString
+ Data.Apiary.Param: instance [overlap ok] Query Double
+ Data.Apiary.Param: instance [overlap ok] Query Float
+ Data.Apiary.Param: instance [overlap ok] Query Int
+ Data.Apiary.Param: instance [overlap ok] Query Int16
+ Data.Apiary.Param: instance [overlap ok] Query Int32
+ Data.Apiary.Param: instance [overlap ok] Query Int64
+ Data.Apiary.Param: instance [overlap ok] Query Int8
+ Data.Apiary.Param: instance [overlap ok] Query Integer
+ Data.Apiary.Param: instance [overlap ok] Query String
+ Data.Apiary.Param: instance [overlap ok] Query Text
+ Data.Apiary.Param: instance [overlap ok] Query Word
+ Data.Apiary.Param: instance [overlap ok] Query Word16
+ Data.Apiary.Param: instance [overlap ok] Query Word32
+ Data.Apiary.Param: instance [overlap ok] Query Word64
+ Data.Apiary.Param: instance [overlap ok] Query Word8
+ Data.Apiary.Param: instance [overlap ok] Query a => Query (Maybe a)
+ Data.Apiary.Param: instance [overlap ok] Query a => ReqParam a
+ Data.Apiary.Param: instance [overlap ok] ReqParam (FileInfo ByteString)
+ Data.Apiary.Param: pFile :: Proxy (File ByteString)
+ Data.Apiary.Param: reqParams :: ReqParam a => Proxy a -> Request -> [Param] -> [File ByteString] -> [(ByteString, Maybe a)]
- Control.Monad.Apiary.Action: source :: Monad m => Source IO (Flush Builder) -> ActionT m ()
+ Control.Monad.Apiary.Action: source :: Monad m => StreamingBody -> ActionT m ()
- Control.Monad.Apiary.Filter: (=!:) :: (Functor n, Monad n, Query a) => ByteString -> Proxy a -> ApiaryT (Snoc as a) n m b -> ApiaryT as n m b
+ Control.Monad.Apiary.Filter: (=!:) :: (Functor n, MonadIO n, ReqParam a) => ByteString -> Proxy a -> ApiaryT (Snoc as a) n m b -> ApiaryT as n m b
- Control.Monad.Apiary.Filter: (=*:) :: (Functor n, Monad n, Query a) => ByteString -> Proxy a -> ApiaryT (Snoc as [a]) n m b -> ApiaryT as n m b
+ Control.Monad.Apiary.Filter: (=*:) :: (Functor n, MonadIO n, ReqParam a) => ByteString -> Proxy a -> ApiaryT (Snoc as [a]) n m b -> ApiaryT as n m b
- Control.Monad.Apiary.Filter: (=+:) :: (Functor n, Monad n, Query a) => ByteString -> Proxy a -> ApiaryT (Snoc as [a]) n m b -> ApiaryT as n m b
+ Control.Monad.Apiary.Filter: (=+:) :: (Functor n, MonadIO n, ReqParam a) => ByteString -> Proxy a -> ApiaryT (Snoc as [a]) n m b -> ApiaryT as n m b
- Control.Monad.Apiary.Filter: (=:) :: (Functor n, Monad n, Query a) => ByteString -> Proxy a -> ApiaryT (Snoc as a) n m b -> ApiaryT as n m b
+ Control.Monad.Apiary.Filter: (=:) :: (Functor n, MonadIO n, ReqParam a) => ByteString -> Proxy a -> ApiaryT (Snoc as a) n m b -> ApiaryT as n m b
- Control.Monad.Apiary.Filter: (=?:) :: (Functor n, Monad n, Query a) => ByteString -> Proxy a -> ApiaryT (Snoc as (Maybe a)) n m b -> ApiaryT as n m b
+ Control.Monad.Apiary.Filter: (=?:) :: (Functor n, MonadIO n, ReqParam a) => ByteString -> Proxy a -> ApiaryT (Snoc as (Maybe a)) n m b -> ApiaryT as n m b
- Control.Monad.Apiary.Filter: (?:) :: (Functor n, Monad n, Query a) => ByteString -> Proxy a -> ApiaryT as n m b -> ApiaryT as n m b
+ Control.Monad.Apiary.Filter: (?:) :: (Functor n, MonadIO n, ReqParam a) => ByteString -> Proxy a -> ApiaryT as n m b -> ApiaryT as n m b
- Control.Monad.Apiary.Filter: hasQuery :: (Functor n, Monad n) => ByteString -> ApiaryT c n m a -> ApiaryT c n m a
+ Control.Monad.Apiary.Filter: hasQuery :: (Functor n, MonadIO n) => ByteString -> ApiaryT c n m a -> ApiaryT c n m a
- Control.Monad.Apiary.Filter: query :: (Query a, Strategy w, Functor n, Monad n) => ByteString -> Proxy (w a) -> ApiaryT (SNext w as a) n m b -> ApiaryT as n m b
+ Control.Monad.Apiary.Filter: query :: (ReqParam a, Strategy w, Functor n, MonadIO n) => ByteString -> Proxy (w a) -> ApiaryT (SNext w as a) n m b -> ApiaryT as n m b
Files
- apiary.cabal +22/−8
- src/Control/Monad/Apiary/Action.hs +5/−2
- src/Control/Monad/Apiary/Action/Internal.hs +57/−8
- src/Control/Monad/Apiary/Filter.hs +18/−10
- src/Control/Monad/Apiary/Filter/Internal/Strategy.hs +0/−1
- src/Data/Apiary/Param.hs +18/−0
- src/Web/Apiary/Wai.hs +6/−0
- test/main.hs +5/−5
apiary.cabal view
@@ -1,5 +1,5 @@ name: apiary-version: 0.8.0.0+version: 0.9.0.0 synopsis: Simple web framework inspired by scotty. description: Simple web framework inspired by scotty.@@ -50,8 +50,13 @@ -- extra-source-files: cabal-version: >=1.10 +flag wai3+ description: use wai-3.0+ default: False+ library exposed-modules: Web.Apiary+ Web.Apiary.Wai Control.Monad.Apiary Control.Monad.Apiary.Filter@@ -77,7 +82,6 @@ build-depends: base >=4.6 && <4.8 , template-haskell >=2.8 && <2.10- , transformers >=0.3 && <0.5 , mtl >=2.1 && <2.3 , monad-control >=0.3 && <0.4 , transformers-base >=0.4 && <0.5@@ -85,16 +89,21 @@ , text >=1.1 && <1.2 , bytestring >=0.10 && <0.11 , blaze-builder >=0.3 && <0.4- , conduit >=1.1 && <1.2 , data-default-class >=0.0 && <0.1 , tagged >=0.7 && <0.8 , reflection >=1.4 && <1.5 , http-types >=0.8 && <0.9 , mime-types >=0.1 && <0.2- , wai >=2.1 && <2.2- , warp >=2.1 && <2.2 , exceptions >=0.6 && <0.7+ if flag(wai3)+ build-depends: wai >=3.0 && <3.1+ , wai-extra >=3.0 && <3.1+ cpp-options: -DWAI3+ else+ build-depends: wai >=2.1 && <2.2+ , wai-extra >=2.1 && <2.2+ , conduit >=1.1 && <1.2 hs-source-dirs: src ghc-options: -O2 -Wall@@ -106,11 +115,16 @@ build-depends: base >=4.6 && <4.8 , test-framework >=0.8 && <0.9 , test-framework-hunit >=0.3 && <0.4- , wai >=2.1 && <2.2- , wai-test >=2.0 && <2.1 , apiary , bytestring >=0.10 && <0.11- , http-types >=0.8 && <0.9+ , http-types >=0.8 && <0.9+ if flag(wai3)+ build-depends: wai >=3.0 && <3.1+ , wai-test >=3.0 && <3.1+ else+ build-depends: wai >=2.1 && <2.2+ , wai-test >=2.0 && <2.1+ hs-source-dirs: test ghc-options: -O2 -Wall -fno-warn-missing-signatures default-language: Haskell2010
src/Control/Monad/Apiary/Action.hs view
@@ -8,6 +8,8 @@ -- ** getter , getRequest , getHeaders+ , getReqParams+ , getReqFiles -- ** setter , status@@ -19,7 +21,8 @@ , file' , builder , lbs- , source+ , stream+ , StreamingBody -- ** monolithic action -- *** redirect@@ -29,7 +32,7 @@ , module Data.Default.Class -- * deprecated- , redirectFound, redirectSeeOther+ , redirectFound, redirectSeeOther, source ) where import Control.Monad.Apiary.Action.Internal
src/Control/Monad/Apiary/Action/Internal.hs view
@@ -6,6 +6,7 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE Rank2Types #-} {-# LANGUAGE NoMonomorphismRestriction #-}+{-# LANGUAGE CPP #-} module Control.Monad.Apiary.Action.Internal where @@ -17,6 +18,7 @@ import Control.Monad.Catch import Control.Monad.Trans.Control import Network.Wai+import Network.Wai.Parse import Network.Mime import Data.Default.Class import Network.HTTP.Types@@ -24,7 +26,10 @@ import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L import qualified Data.Text as T++#ifndef WAI3 import Data.Conduit+#endif data ApiaryConfig = ApiaryConfig { -- | call when no handler matched.@@ -38,10 +43,16 @@ , mimeType :: FilePath -> S.ByteString } +defNotFound :: Application+#ifdef WAI3+defNotFound _ f = f $ responseLBS status404 [("Content-Type", "text/plain")] "404 Page Notfound.\n"+#else+defNotFound _ = return $ responseLBS status404 [("Content-Type", "text/plain")] "404 Page Notfound.\n"+#endif+ instance Default ApiaryConfig where def = ApiaryConfig - { notFound = \_ -> return $ responseLBS status404 - [("Content-Type", "text/plain")] "404 Page Notfound.\n"+ { notFound = defNotFound , defaultStatus = ok200 , defaultHeader = [] , rootPattern = ["", "/", "/index.html", "/index.htm"]@@ -53,20 +64,29 @@ { actionStatus :: Status , actionHeaders :: ResponseHeaders , actionBody :: Body+ , actionReqBody :: Maybe ([Param], [File L.ByteString]) } +#ifndef WAI3+type StreamingBody = Source IO (Flush Builder)+#endif+ data Body = File FilePath (Maybe FilePart) | Builder Builder | LBS L.ByteString- | SRC (Source IO (Flush Builder))+ | Str StreamingBody actionStateToResponse :: ActionState -> Response actionStateToResponse as = case actionBody as of File f p -> responseFile st hd f p Builder b -> responseBuilder st hd b LBS l -> responseLBS st hd l- SRC s -> responseSource st hd s+#ifdef WAI3+ Str s -> responseStream st hd s+#else+ Str s -> responseSource st hd s+#endif where st = actionStatus as hd = actionHeaders as@@ -134,12 +154,22 @@ hoistActionT run m = actionT $ \c r s -> run (runActionT m c r s) execActionT :: ApiaryConfig -> ActionT IO () -> Application++#ifdef WAI3+execActionT config m request send = runActionT m config request resp >>= \case+ Pass -> notFound config request send+ Stop s -> send s+ Continue (_,r) -> send $ actionStateToResponse r+ where+ resp = ActionState (defaultStatus config) (defaultHeader config) (LBS "") Nothing+#else execActionT config m request = runActionT m config request resp >>= \case Pass -> notFound config request Stop s -> return s Continue (_,r) -> return $ actionStateToResponse r where- resp = ActionState (defaultStatus config) (defaultHeader config) (LBS "")+ resp = ActionState (defaultStatus config) (defaultHeader config) (LBS "") Nothing+#endif instance (Monad m, Functor m) => Alternative (ActionT m) where empty = mzero@@ -182,6 +212,21 @@ getRequest :: Monad m => ActionT m Request getRequest = ActionT $ \_ r s c -> c r s +getRequestBody :: MonadIO m => ActionT m ([Param], [File L.ByteString])+getRequestBody = ActionT $ \_ r s c -> case actionReqBody s of+ Just b -> c b s+ Nothing -> do+ b <- liftIO $ parseRequestBody lbsBackEnd r+ c b s { actionReqBody = Just b }++-- | parse request body and return params. since 0.9.0.0.+getReqParams :: MonadIO m => ActionT m [Param]+getReqParams = fst <$> getRequestBody++-- | parse request body and return files. since 0.9.0.0.+getReqFiles :: MonadIO m => ActionT m [File L.ByteString]+getReqFiles = snd <$> getRequestBody+ getConfig :: Monad m => ActionT m ApiaryConfig getConfig = ActionT $ \c _ s cont -> cont c s @@ -285,9 +330,13 @@ lbs :: Monad m => L.ByteString -> ActionT m () lbs l = modifyState (\s -> s { actionBody = LBS l } ) --- | set response body source. since 0.1.0.0.-source :: Monad m => Source IO (Flush Builder) -> ActionT m ()-source src = modifyState (\s -> s { actionBody = SRC src } )+-- | set response body source. since 0.9.0.0.+stream :: Monad m => StreamingBody -> ActionT m ()+stream str = modifyState (\s -> s { actionBody = Str str })++{-# DEPRECATED source "use stream" #-}+source :: Monad m => StreamingBody -> ActionT m ()+source = stream {-# DEPRECATED redirectFound, redirectSeeOther "use redirect" #-} -- | redirect with 302 Found. since 0.3.3.0.
src/Control/Monad/Apiary/Filter.hs view
@@ -5,6 +5,7 @@ {-# LANGUAGE Rank2Types #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE ScopedTypeVariables #-} module Control.Monad.Apiary.Filter ( -- * filters@@ -42,6 +43,7 @@ ) where import Control.Monad+import Control.Monad.Trans import Network.Wai as Wai import qualified Network.HTTP.Types as HT import Network.HTTP.Types (StdMethod(..))@@ -107,20 +109,26 @@ -- query "key" (Proxy :: Proxy ('Option' (Maybe Int)) -- get first \'key\' query parameter as Int. allow without param or value. -- query "key" (Proxy :: Proxy ('Many' String) -- get all \'key\' query parameter as String. -- @--- -query :: (Query a, Strategy.Strategy w, Functor n, Monad n)+query :: forall a as w n m b. + (ReqParam a, Strategy.Strategy w, Functor n, MonadIO n) => S.ByteString -> Proxy (w a) -> ApiaryT (Strategy.SNext w as a) n m b -> ApiaryT as n m b-query k p = function $ \l r -> Strategy.readStrategy readQuery ((k ==) . fst) p (queryString r) l+query key p = focus $ \l -> do+ r <- getRequest+ (q,f) <- getRequestBody + maybe mzero return $+ Strategy.readStrategy id ((key ==) . fst) p + (reqParams (Proxy :: Proxy a) r q f) l+ -- | get first matched paramerer. since 0.5.0.0. -- -- @ -- "key" =: pInt == query "key" (pFirst pInt) == query "key" (Proxy :: Proxy (First Int)) -- @-(=:) :: (Functor n, Monad n, Query a) => S.ByteString -> Proxy a +(=:) :: (Functor n, MonadIO n, ReqParam a) => S.ByteString -> Proxy a -> ApiaryT (Snoc as a) n m b -> ApiaryT as n m b k =: t = query k (pFirst t) @@ -131,7 +139,7 @@ -- @ -- "key" =: pInt == query "key" (pOne pInt) == query "key" (Proxy :: Proxy (One Int)) -- @-(=!:) :: (Functor n, Monad n, Query a) => S.ByteString -> Proxy a +(=!:) :: (Functor n, MonadIO n, ReqParam a) => S.ByteString -> Proxy a -> ApiaryT (Snoc as a) n m b -> ApiaryT as n m b k =!: t = query k (pOne t) @@ -142,7 +150,7 @@ -- @ -- "key" =: pInt == query "key" (pOption pInt) == query "key" (Proxy :: Proxy (Option Int)) -- @-(=?:) :: (Functor n, Monad n, Query a) => S.ByteString -> Proxy a +(=?:) :: (Functor n, MonadIO n, ReqParam a) => S.ByteString -> Proxy a -> ApiaryT (Snoc as (Maybe a)) n m b -> ApiaryT as n m b k =?: t = query k (pOption t) @@ -153,7 +161,7 @@ -- @ -- "key" =: pInt == query "key" (pCheck pInt) == query "key" (Proxy :: Proxy (Check Int)) -- @-(?:) :: (Functor n, Monad n, Query a) => S.ByteString -> Proxy a +(?:) :: (Functor n, MonadIO n, ReqParam a) => S.ByteString -> Proxy a -> ApiaryT as n m b -> ApiaryT as n m b k ?: t = query k (pCheck t) @@ -162,7 +170,7 @@ -- @ -- "key" =: pInt == query "key" (pMany pInt) == query "key" (Proxy :: Proxy (Many Int)) -- @-(=*:) :: (Functor n, Monad n, Query a) => S.ByteString -> Proxy a +(=*:) :: (Functor n, MonadIO n, ReqParam a) => S.ByteString -> Proxy a -> ApiaryT (Snoc as [a]) n m b -> ApiaryT as n m b k =*: t = query k (pMany t) @@ -171,7 +179,7 @@ -- @ -- "key" =: pInt == query "key" (pSome pInt) == query "key" (Proxy :: Proxy (Some Int)) -- @-(=+:) :: (Functor n, Monad n, Query a) => S.ByteString -> Proxy a +(=+:) :: (Functor n, MonadIO n, ReqParam a) => S.ByteString -> Proxy a -> ApiaryT (Snoc as [a]) n m b -> ApiaryT as n m b k =+: t = query k (pSome t) @@ -181,7 +189,7 @@ -- hasQuery q = 'query' q (Proxy :: Proxy ('Check' ())) -- @ ---hasQuery :: (Functor n, Monad n) => S.ByteString -> ApiaryT c n m a -> ApiaryT c n m a+hasQuery :: (Functor n, MonadIO n) => S.ByteString -> ApiaryT c n m a -> ApiaryT c n m a hasQuery q = query q (Proxy :: Proxy (Strategy.Check ())) --------------------------------------------------------------------------------
src/Control/Monad/Apiary/Filter/Internal/Strategy.hs view
@@ -27,7 +27,6 @@ getQuery :: (v -> Maybe a) -> Proxy (w a) -> ((k,v) -> Bool) -> [(k, v)] -> [Maybe a] getQuery readf _ kf = map readf . map snd . filter kf - -- | get first matched key( [1,) params to Type.). since 0.5.0.0. data Option a instance Strategy Option where
src/Data/Apiary/Param.hs view
@@ -6,6 +6,8 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE OverlappingInstances #-} module Data.Apiary.Param where @@ -22,6 +24,9 @@ import Data.Proxy import Data.String(IsString) +import Network.Wai+import Network.Wai.Parse+ jsToBool :: (IsString a, Eq a) => a -> Bool jsToBool = flip notElem jsFalse where@@ -149,3 +154,16 @@ pMaybe :: Proxy a -> Proxy (Maybe a) pMaybe _ = Proxy++pFile :: Proxy (File L.ByteString)+pFile = Proxy++class ReqParam a where+ reqParams :: Proxy a -> Request -> [Param] -> [File L.ByteString] -> [(S.ByteString, Maybe a)]++instance ReqParam (FileInfo L.ByteString) where+ reqParams _ _ _ f = map (\(k,v) -> (k, Just v)) f++instance Query a => ReqParam a where+ reqParams _ r p _ = map (\(k,v) -> (k, readQuery v)) (queryString r) +++ map (\(k,v) -> (k, readQuery $ Just v)) p
+ src/Web/Apiary/Wai.hs view
@@ -0,0 +1,6 @@+-- | reexporting Network.Wai to reduce dependencies from apiary-* packages.+module Web.Apiary.Wai+ ( module Network.Wai+ ) where++import Network.Wai
test/main.hs view
@@ -153,7 +153,7 @@ where app = queryApp (=:) (=:) (=:) queryOneTest :: Test-queryOneTest = testGroup "First"+queryOneTest = testGroup "One" [ testReq "GET /" $ assert404 app , testReq "GET /?foo" $ assertPlain200 "foo Maybe String Nothing" app , testReq "GET /?foo&foo=3" $ assert404 app@@ -165,7 +165,7 @@ where app = queryApp (=!:) (=!:) (=!:) queryOptionTest :: Test-queryOptionTest = testGroup "First"+queryOptionTest = testGroup "Option" [ testReq "GET /" $ assertPlain200 "foo Int Nothing" app , testReq "GET /?foo" $ assertPlain200 "foo Maybe String Just Nothing" app , testReq "GET /?foo&foo=3" $ assertPlain200 "foo Maybe String Just Nothing" app@@ -177,7 +177,7 @@ where app = queryApp (=?:) (=?:) (=?:) queryCheckTest :: Test-queryCheckTest = testGroup "First"+queryCheckTest = testGroup "Check" [ testReq "GET /" $ assert404 app , testReq "GET /?foo" $ assertPlain200 "foo Maybe String" app , testReq "GET /?foo&foo=3" $ assertPlain200 "foo Maybe String" app@@ -189,7 +189,7 @@ where app = queryCheckApp queryManyTest :: Test-queryManyTest = testGroup "First"+queryManyTest = testGroup "Many" [ testReq "GET /" $ assertPlain200 "foo Int []" app , testReq "GET /?foo" $ assertPlain200 "foo Maybe String [Nothing]" app , testReq "GET /?foo&foo=3" $ assertPlain200 "foo Maybe String [Nothing,Just \"3\"]" app@@ -201,7 +201,7 @@ where app = queryApp (=*:) (=*:) (=*:) querySomeTest :: Test-querySomeTest = testGroup "First"+querySomeTest = testGroup "Some" [ testReq "GET /" $ assert404 app , testReq "GET /?foo" $ assertPlain200 "foo Maybe String [Nothing]" app , testReq "GET /?foo&foo=3" $ assertPlain200 "foo Maybe String [Nothing,Just \"3\"]" app