add captureCombinator

This commit is contained in:
Sönke Hahn 2016-10-23 14:00:52 -04:00
parent 447a807cf0
commit 16cffc7d69
3 changed files with 65 additions and 11 deletions

View file

@ -45,6 +45,7 @@ library
exposed-modules: exposed-modules:
Servant Servant
Servant.Server Servant.Server
Servant.Server.CombinatorUtils
Servant.Server.Experimental.Auth Servant.Server.Experimental.Auth
Servant.Server.Internal Servant.Server.Internal
Servant.Server.Internal.BasicAuth Servant.Server.Internal.BasicAuth
@ -132,10 +133,12 @@ test-suite spec
main-is: Spec.hs main-is: Spec.hs
other-modules: other-modules:
Servant.ArbitraryMonadServerSpec Servant.ArbitraryMonadServerSpec
Servant.Server.CombinatorUtilsSpec
Servant.Server.ErrorSpec Servant.Server.ErrorSpec
Servant.Server.Internal.ContextSpec Servant.Server.Internal.ContextSpec
Servant.Server.Internal.RoutingApplicationSpec Servant.Server.Internal.RoutingApplicationSpec
Servant.Server.RouterSpec Servant.Server.RouterSpec
Servant.ServerSpec
Servant.Server.StreamingSpec Servant.Server.StreamingSpec
Servant.Server.UsingContextSpec Servant.Server.UsingContextSpec
Servant.Server.UsingContextSpec.TestCombinators Servant.Server.UsingContextSpec.TestCombinators

View file

@ -5,9 +5,11 @@
module Servant.Server.CombinatorUtils ( module Servant.Server.CombinatorUtils (
RouteResult(..), RouteResult(..),
argumentCombinator, argumentCombinator,
captureCombinator,
) where ) where
import Data.Proxy import Data.Proxy
import Data.Text
import Network.Wai import Network.Wai
import Servant.API import Servant.API
@ -24,5 +26,19 @@ argumentCombinator ::
-> Delayed env (Server (combinator :> api)) -> Delayed env (Server (combinator :> api))
-> Router' env RoutingApplication -> Router' env RoutingApplication
argumentCombinator getArg Proxy context delayed = argumentCombinator getArg Proxy context delayed =
route (Proxy :: Proxy api) context $ addBodyCheck delayed $ route (Proxy :: Proxy api) context $ addBodyCheck delayed
DelayedIO $ \ request -> return $ getArg request (DelayedIO (return ())) $ \ () ->
withRequest $ \ request -> liftRouteResult (getArg request)
captureCombinator ::
forall api combinator arg context env .
(HasServer api context) =>
(Text -> RouteResult arg)
-> Proxy (combinator :> api)
-> Context context
-> Delayed env (arg -> Server api)
-> Router' env RoutingApplication
captureCombinator getArg Proxy context delayed =
CaptureRouter $
route (Proxy :: Proxy api) context $ addCapture delayed $ \ captured ->
(liftRouteResult (getArg captured))

View file

@ -8,12 +8,13 @@
module Servant.Server.CombinatorUtilsSpec where module Servant.Server.CombinatorUtilsSpec where
import Blaze.ByteString.Builder
import Control.Concurrent import Control.Concurrent
import Data.ByteString.Builder
import Data.ByteString.Lazy import Data.ByteString.Lazy
import Data.Monoid import Data.Monoid
import Data.Proxy import Data.Proxy
import Data.String.Conversions import Data.String.Conversions
import Data.Text
import Network.HTTP.Types import Network.HTTP.Types
import Network.Wai import Network.Wai
import Network.Wai.Internal import Network.Wai.Internal
@ -49,10 +50,10 @@ spec :: Spec
spec = do spec = do
it "allows to write a combinator by providing a function (Request -> a)" $ do it "allows to write a combinator by providing a function (Request -> a)" $ do
let server = return let server = return
app = serve (Proxy :: Proxy (Custom :> Get' String)) server app = serve (Proxy :: Proxy (FooHeader :> Get' String)) server
request = defaultRequest{ request = defaultRequest{
requestHeaders = requestHeaders =
("Custom", "foo") : ("FooHeader", "foo") :
requestHeaders defaultRequest requestHeaders defaultRequest
} }
response <- runApp app request response <- runApp app request
@ -60,23 +61,57 @@ spec = do
it "allows to write a combinator the errors out" $ do it "allows to write a combinator the errors out" $ do
let server = return let server = return
app = serve (Proxy :: Proxy (Custom :> Get' String)) server app = serve (Proxy :: Proxy (FooHeader :> Get' String)) server
request = defaultRequest request = defaultRequest
response <- runApp app request response <- runApp app request
responseStatus response `shouldBe` status400 responseStatus response `shouldBe` status400
it "allows to pick the phase of request consumption" $ do it "allows to write capture combinators" $ do
let server = return
app = serve (Proxy :: Proxy (StringCapture :> Get' String)) server
request = defaultRequest{
rawPathInfo = "/foo",
pathInfo = ["foo"]
}
response <- runApp app request
responseBodyLbs response `shouldReturn` "\"foo\""
it "allows to pick the request check phase" $ do
pending
it "allows to write streaming combinators for request bodies" $ do
pending
it "disallows to access the request body unless in the checkBody phase" $ do
pending
it "allows to access the context" $ do
pending
it "allows to write combinators without args" $ do
pending
it "allows to implement combinators based in terms of existing combinators" $ do
pending pending
type Get' = Get '[JSON] type Get' = Get '[JSON]
data Custom data FooHeader
instance HasServer api context => HasServer (Custom :> api) context where instance HasServer api context => HasServer (FooHeader :> api) context where
type ServerT (Custom :> api) m = String -> ServerT api m type ServerT (FooHeader :> api) m = String -> ServerT api m
route = argumentCombinator getCustom route = argumentCombinator getCustom
getCustom :: Request -> RouteResult String getCustom :: Request -> RouteResult String
getCustom request = case lookup "Custom" (requestHeaders request) of getCustom request = case lookup "FooHeader" (requestHeaders request) of
Nothing -> FailFatal err400 Nothing -> FailFatal err400
Just l -> Route $ cs l Just l -> Route $ cs l
data StringCapture
instance HasServer api context => HasServer (StringCapture :> api) context where
type ServerT (StringCapture :> api) m = String -> ServerT api m
route = captureCombinator getCapture
getCapture :: Text -> RouteResult String
getCapture = Route . cs