on master help sign in
provide convenience functions for ClientM-based clients
commitb592b51ed81654a7ee85a4aa8d85ec8286f6cf3e authorArnaud Bailly <arnaud.oqube@gmail.com> dateMon 4 Sep 2017, 09:19 (9 years ago) parent7480076
3 files changed+32−20
servant-client/src/Servant/Client.hs+18−3 servant-client/src/Servant/Client/HttpClient.hs−3 servant-client/test/Servant/ClientSpec.hs+14−14
servant-client/src/Servant/Client.hs+18−3
@@ -22,7 +22,7 @@ module Servant.Client
2222 , client
2323 , HasClient(..)
2424 , ClientM
25− , runClientM
25+ , runClientM, inClientM, clientM
2626 , ClientEnv (ClientEnv)
2727 , mkAuthenticateReq
2828 , ServantError(..)
@@ -52,7 +52,8 @@ import Servant.Common.Req
5252
5353-- * Accessing APIs as a Client
5454
55−-- | 'client' allows you to produce operations to query an API from a client.
55+-- | 'client' allows you to produce operations to query an API from a client within
56+-- a given monadic context `m`
5657--
5758-- > type MyApi = "books" :> Get '[JSON] [Book] -- GET /books
5859-- > :<|> "books" :> ReqBody '[JSON] Book :> Post '[JSON] Book -- POST /books
@@ -60,12 +61,26 @@ import Servant.Common.Req
6061-- > myApi :: Proxy MyApi
6162-- > myApi = Proxy
6263-- >
64+-- > clientM :: Proxy ClientM
65+-- > clientM = Proxy
66+-- >
6367-- > getAllBooks :: ClientM [Book]
6468-- > postNewBook :: Book -> ClientM Book
65−-- > (getAllBooks :<|> postNewBook) = client myApi
69+-- > (getAllBooks :<|> postNewBook) = client clientM myApi
6670client :: HasClient m api => Proxy m -> Proxy api -> Client m api
6771client pm p = clientWithRoute pm p defReq
6872
73+-- | Helper proxy to simplify common case of working in `ClientM` monad
74+inClientM :: Proxy ClientM
75+inClientM = Proxy
76+
77+-- | Convenience method to declare clients running in the `ClientM` monad.
78+--
79+-- Simply pass `inClientM` to `client`....
80+clientM :: (HasClient ClientM api) => Proxy api -> Client ClientM api
81+clientM = client inClientM
82+
83+
6984-- | This class lets us define how each API combinator
7085-- influences the creation of an HTTP request. It's mostly
7186-- an internal class, you can just use 'client'.
servant-client/src/Servant/Client/HttpClient.hs−3
@@ -63,7 +63,6 @@ data ClientEnv
6363
6464-- | @ClientM@ is the monad in which client functions run. Contains the
6565-- 'Manager' and 'BaseUrl' used for requests in the reader environment.
66−
6766newtype ClientM a = ClientM { runClientM' :: ReaderT ClientEnv (ExceptT ServantError IO) a }
6867 deriving ( Functor, Applicative, Monad, MonadIO, Generic
6968 , MonadReader ClientEnv
@@ -77,10 +76,8 @@ instance MonadBase IO ClientM where
7776instance MonadBaseControl IO ClientM where
7877 type StM ClientM a = Either ServantError a
7978
80− -- liftBaseWith :: (RunInBase ClientM IO -> IO a) -> ClientM a
8179 liftBaseWith f = ClientM (liftBaseWith (\g -> f (g . runClientM')))
8280
83− -- restoreM :: StM ClientM a -> ClientM a
8481 restoreM st = ClientM (restoreM st)
8582
8683-- | Try clients in order, last error is preserved.
servant-client/test/Servant/ClientSpec.hs+14−14
@@ -63,7 +63,7 @@ import Servant.Server
6363import Servant.Server.Experimental.Auth
6464
6565-- This declaration simply checks that all instances are in place.
66−_ = client (Proxy :: Proxy ClientM) comprehensiveAPI
66+_ = client inClientM comprehensiveAPI
6767
6868spec :: Spec
6969spec = describe "Servant.Client" $ do
@@ -147,7 +147,7 @@ getGet
147147 :<|> getMultiple
148148 :<|> getRespHeaders
149149 :<|> getDeleteContentType
150− :<|> EmptyClient = client (Proxy :: Proxy ClientM) api
150+ :<|> EmptyClient = client inClientM api
151151
152152server :: Application
153153server = serve api (
@@ -359,7 +359,7 @@ wrappedApiSpec = describe "error status codes" $ do
359359 test (WrappedApi api, desc) =
360360 it desc $ bracket (startWaiApp $ serveW api) endWaiApp $ \(_, baseUrl) -> do
361361 let getResponse :: SCR.ClientM ()
362− getResponse = client (Proxy :: Proxy ClientM) api
362+ getResponse = client inClientM api
363363 Left FailureResponse{..} <- runClientM getResponse (ClientEnv manager baseUrl)
364364 responseStatus `shouldBe` (HTTP.Status 500 "error message")
365365 in mapM_ test $
@@ -374,35 +374,35 @@ failSpec = beforeAll (startWaiApp failServer) $ afterAll endWaiApp $ do
374374
375375 context "client returns errors appropriately" $ do
376376 it "reports FailureResponse" $ \(_, baseUrl) -> do
377− let (_ :<|> getDeleteEmpty :<|> _) = client (Proxy :: Proxy ClientM) api
377+ let (_ :<|> getDeleteEmpty :<|> _) = client inClientM api
378378 Left res <- runClientM getDeleteEmpty (ClientEnv manager baseUrl)
379379 case res of
380380 FailureResponse _ (HTTP.Status 404 "Not Found") _ _ -> return ()
381381 _ -> fail $ "expected 404 response, but got " <> show res
382382
383383 it "reports DecodeFailure" $ \(_, baseUrl) -> do
384− let (_ :<|> _ :<|> getCapture :<|> _) = client (Proxy :: Proxy ClientM) api
384+ let (_ :<|> _ :<|> getCapture :<|> _) = client inClientM api
385385 Left res <- runClientM (getCapture "foo") (ClientEnv manager baseUrl)
386386 case res of
387387 DecodeFailure _ ("application/json") _ -> return ()
388388 _ -> fail $ "expected DecodeFailure, but got " <> show res
389389
390390 it "reports ConnectionError" $ \_ -> do
391− let (getGetWrongHost :<|> _) = client (Proxy :: Proxy ClientM) api
391+ let (getGetWrongHost :<|> _) = client inClientM api
392392 Left res <- runClientM getGetWrongHost (ClientEnv manager (BaseUrl Http "127.0.0.1" 19872 ""))
393393 case res of
394394 ConnectionError _ -> return ()
395395 _ -> fail $ "expected ConnectionError, but got " <> show res
396396
397397 it "reports UnsupportedContentType" $ \(_, baseUrl) -> do
398− let (getGet :<|> _ ) = client (Proxy :: Proxy ClientM) api
398+ let (getGet :<|> _ ) = client inClientM api
399399 Left res <- runClientM getGet (ClientEnv manager baseUrl)
400400 case res of
401401 UnsupportedContentType ("application/octet-stream") _ -> return ()
402402 _ -> fail $ "expected UnsupportedContentType, but got " <> show res
403403
404404 it "reports InvalidContentTypeHeader" $ \(_, baseUrl) -> do
405− let (_ :<|> _ :<|> _ :<|> _ :<|> getBody :<|> _) = client (Proxy :: Proxy ClientM) api
405+ let (_ :<|> _ :<|> _ :<|> _ :<|> getBody :<|> _) = client inClientM api
406406 Left res <- runClientM (getBody alice) (ClientEnv manager baseUrl)
407407 case res of
408408 InvalidContentTypeHeader "fooooo" _ -> return ()
@@ -418,14 +418,14 @@ basicAuthSpec = beforeAll (startWaiApp basicAuthServer) $ afterAll endWaiApp $ d
418418 context "Authentication works when requests are properly authenticated" $ do
419419
420420 it "Authenticates a BasicAuth protected server appropriately" $ \(_,baseUrl) -> do
421− let getBasic = client (Proxy :: Proxy ClientM) basicAuthAPI
421+ let getBasic = client inClientM basicAuthAPI
422422 let basicAuthData = BasicAuthData "servant" "server"
423423 (left show <$> runClientM (getBasic basicAuthData) (ClientEnv manager baseUrl)) `shouldReturn` Right alice
424424
425425 context "Authentication is rejected when requests are not authenticated properly" $ do
426426
427427 it "Authenticates a BasicAuth protected server appropriately" $ \(_,baseUrl) -> do
428− let getBasic = client (Proxy :: Proxy ClientM) basicAuthAPI
428+ let getBasic = client inClientM basicAuthAPI
429429 let basicAuthData = BasicAuthData "not" "password"
430430 Left FailureResponse{..} <- runClientM (getBasic basicAuthData) (ClientEnv manager baseUrl)
431431 responseStatus `shouldBe` HTTP.Status 403 "Forbidden"
@@ -435,14 +435,14 @@ genAuthSpec = beforeAll (startWaiApp genAuthServer) $ afterAll endWaiApp $ do
435435 context "Authentication works when requests are properly authenticated" $ do
436436
437437 it "Authenticates a AuthProtect protected server appropriately" $ \(_, baseUrl) -> do
438− let getProtected = client (Proxy :: Proxy ClientM) genAuthAPI
438+ let getProtected = client inClientM genAuthAPI
439439 let authRequest = mkAuthenticateReq () (\_ req -> SCR.addHeader "AuthHeader" ("cool" :: String) req)
440440 (left show <$> runClientM (getProtected authRequest) (ClientEnv manager baseUrl)) `shouldReturn` Right alice
441441
442442 context "Authentication is rejected when requests are not authenticated properly" $ do
443443
444444 it "Authenticates a AuthProtect protected server appropriately" $ \(_, baseUrl) -> do
445− let getProtected = client (Proxy :: Proxy ClientM) genAuthAPI
445+ let getProtected = client inClientM genAuthAPI
446446 let authRequest = mkAuthenticateReq () (\_ req -> SCR.addHeader "Wrong" ("header" :: String) req)
447447 Left FailureResponse{..} <- runClientM (getProtected authRequest) (ClientEnv manager baseUrl)
448448 responseStatus `shouldBe` (HTTP.Status 401 "Unauthorized")
@@ -451,11 +451,11 @@ genericClientSpec :: Spec
451451genericClientSpec = beforeAll (startWaiApp genericClientServer) $ afterAll endWaiApp $ do
452452 describe "Servant.Client.Generic" $ do
453453
454− let GenericClient{..} = mkClient (client (Proxy :: Proxy ClientM) (Proxy :: Proxy GenericClientAPI))
454+ let GenericClient{..} = mkClient (client inClientM (Proxy :: Proxy GenericClientAPI))
455455 NestedClient1{..} = mkNestedClient1 "example"
456456 NestedClient2{..} = mkNestedClient2 (Just 42)
457457
458− it "works for top-level client (Proxy :: Proxy ClientM) function" $ \(_, baseUrl) -> do
458+ it "works for top-level client inClientM function" $ \(_, baseUrl) -> do
459459 (left show <$> (runClientM (getSqr (Just 5)) (ClientEnv manager baseUrl))) `shouldReturn` Right 25
460460
461461 it "works for nested clients" $ \(_, baseUrl) -> do