servant-client 0.10 → 0.11
raw patch · 5 files changed
+70/−26 lines, 5 filesdep ~basedep ~http-api-datadep ~http-media
Dependency ranges changed: base, http-api-data, http-media, http-types, servant, servant-server
Files
- CHANGELOG.md +12/−0
- servant-client.cabal +8/−8
- src/Servant/Client.hs +18/−0
- src/Servant/Common/Req.hs +18/−8
- test/Servant/ClientSpec.hs +14/−10
CHANGELOG.md view
@@ -1,3 +1,15 @@+0.11+----++### Other changes++- Path components are escaped+ ([#696](https://github.com/haskell-servant/servant/pull/696))+- `Req` `reqPath` field changed from `String` to `BS.Builder`+ ([#696](https://github.com/haskell-servant/servant/pull/696))+- Include `Req` in failure errors+ ([#740](https://github.com/haskell-servant/servant/pull/740))+ 0.10 -----
servant-client.cabal view
@@ -1,5 +1,5 @@ name: servant-client-version: 0.10+version: 0.11 synopsis: automatical derivation of querying functions for servant webservices description: This library lets you derive automatically Haskell functions that@@ -38,13 +38,13 @@ build-depends: base >= 4.7 && < 4.10 , base-compat >= 0.9.1 && < 0.10- , aeson >= 0.7 && < 1.2+ , aeson >= 0.7 && < 1.3 , attoparsec >= 0.12 && < 0.14 , base64-bytestring >= 1.0.0.1 && < 1.1 , bytestring >= 0.10 && < 0.11 , exceptions >= 0.8 && < 0.9- , generics-sop >= 0.1.0.0 && < 0.3- , http-api-data >= 0.3 && < 0.4+ , generics-sop >= 0.1.0.0 && < 0.4+ , http-api-data >= 0.3.6 && < 0.4 , http-client >= 0.4.18.1 && < 0.6 , http-client-tls >= 0.2.2 && < 0.4 , http-media >= 0.6.2 && < 0.7@@ -52,8 +52,8 @@ , monad-control >= 1.0.0.4 && < 1.1 , network-uri >= 2.6 && < 2.7 , safe >= 0.3.9 && < 0.4- , semigroupoids >= 4.3 && < 5.2- , servant == 0.10.*+ , semigroupoids >= 4.3 && < 5.3+ , servant == 0.11.* , string-conversions >= 0.3 && < 0.5 , text >= 1.2 && < 1.3 , transformers >= 0.3 && < 0.6@@ -94,9 +94,9 @@ , mtl , network >= 2.6 , QuickCheck >= 2.7- , servant == 0.10.*+ , servant , servant-client- , servant-server == 0.10.*+ , servant-server == 0.11.* , text , transformers , transformers-compat
src/Servant/Client.hs view
@@ -24,6 +24,7 @@ , ClientEnv (ClientEnv) , mkAuthenticateReq , ServantError(..)+ , EmptyClient(..) , module Servant.Common.BaseUrl ) where @@ -87,6 +88,23 @@ clientWithRoute Proxy req = clientWithRoute (Proxy :: Proxy a) req :<|> clientWithRoute (Proxy :: Proxy b) req++-- | Singleton type representing a client for an empty API.+data EmptyClient = EmptyClient deriving (Eq, Show, Bounded, Enum)++-- | The client for 'EmptyAPI' is simply 'EmptyClient'.+--+-- > type MyAPI = "books" :> Get '[JSON] [Book] -- GET /books+-- > :<|> "nothing" :> EmptyAPI+-- >+-- > myApi :: Proxy MyApi+-- > myApi = Proxy+-- >+-- > getAllBooks :: ClientM [Book]+-- > (getAllBooks :<|> EmptyClient) = client myApi+instance HasClient EmptyAPI where+ type Client EmptyAPI = EmptyClient+ clientWithRoute Proxy _ = EmptyClient -- | If you use a 'Capture' in one of your endpoints in your API, -- the corresponding querying function will automatically take
src/Servant/Common/Req.hs view
@@ -27,6 +27,7 @@ import Control.Monad.IO.Class () import Control.Monad.Reader import Control.Monad.Trans.Control (MonadBaseControl (..))+import qualified Data.ByteString.Builder as BS import Data.ByteString.Lazy hiding (pack, filter, map, null, elem, any) import Data.String import Data.String.Conversions (cs)@@ -48,7 +49,8 @@ data ServantError = FailureResponse- { responseStatus :: Status+ { failingRequest :: UrlReq+ , responseStatus :: Status , responseContentType :: MediaType , responseBody :: ByteString }@@ -71,7 +73,7 @@ deriving (Show, Typeable) instance Eq ServantError where- FailureResponse a b c == FailureResponse x y z =+ FailureResponse _ a b c == FailureResponse _ x y z = (a, b, c) == (x, y, z) DecodeFailure a b c == DecodeFailure x y z = (a, b, c) == (x, y, z)@@ -85,8 +87,15 @@ instance Exception ServantError +data UrlReq = UrlReq BaseUrl Req++instance Show UrlReq where+ show (UrlReq url req) = showBaseUrl url ++ path ++ "?" ++ show (qs req)+ where+ path = cs (BS.toLazyByteString (reqPath req))+ data Req = Req- { reqPath :: String+ { reqPath :: BS.Builder , qs :: QueryText , reqBody :: Maybe (RequestBody, MediaType) , reqAccept :: [MediaType]@@ -98,7 +107,7 @@ appendToPath :: String -> Req -> Req appendToPath p req =- req { reqPath = reqPath req ++ "/" ++ p }+ req { reqPath = reqPath req <> "/" <> toEncodedUrlPiece p } appendToQueryString :: Text -- ^ param name -> Maybe Text -- ^ param value@@ -151,8 +160,9 @@ , uriRegName = reqHost , uriPort = ":" ++ show reqPort }- , uriPath = path ++ reqPath req+ , uriPath = fullPath }+ fullPath = path ++ cs (BS.toLazyByteString (reqPath req)) setrqb r = case reqBody req of Nothing -> r@@ -224,7 +234,7 @@ runClientM cm env = runExceptT $ (flip runReaderT env) $ runClientM' cm -performRequest :: Method -> Req +performRequest :: Method -> Req -> ClientM ( Int, ByteString, MediaType , [HTTP.Header], Response ByteString) performRequest reqMethod req = do@@ -250,10 +260,10 @@ Nothing -> throwError $ InvalidContentTypeHeader (cs t) body Just t' -> pure t' unless (status_code >= 200 && status_code < 300) $- throwError $ FailureResponse status ct body+ throwError $ FailureResponse (UrlReq reqHost req) status ct body return (status_code, body, ct, hdrs, response) -performRequestCT :: MimeUnrender ct result => Proxy ct -> Method -> Req +performRequestCT :: MimeUnrender ct result => Proxy ct -> Method -> Req -> ClientM ([HTTP.Header], result) performRequestCT ct reqMethod req = do let acceptCTS = contentTypes ct
test/Servant/ClientSpec.hs view
@@ -111,6 +111,8 @@ Get '[JSON] (String, Maybe Int, Bool, [(String, [Rational])]) :<|> "headers" :> Get '[JSON] (Headers TestHeaders Bool) :<|> "deleteContentType" :> DeleteNoContent '[JSON] NoContent+ :<|> "empty" :> EmptyAPI+ api :: Proxy Api api = Proxy @@ -122,14 +124,15 @@ getQueryParam :: Maybe String -> SCR.ClientM Person getQueryParams :: [String] -> SCR.ClientM [Person] getQueryFlag :: Bool -> SCR.ClientM Bool-getRawSuccess :: HTTP.Method +getRawSuccess :: HTTP.Method -> SCR.ClientM (Int, BS.ByteString, MediaType, [HTTP.Header], C.Response BS.ByteString)-getRawFailure :: HTTP.Method +getRawFailure :: HTTP.Method -> SCR.ClientM (Int, BS.ByteString, MediaType, [HTTP.Header], C.Response BS.ByteString) getMultiple :: String -> Maybe Int -> Bool -> [(String, [Rational])] -> SCR.ClientM (String, Maybe Int, Bool, [(String, [Rational])]) getRespHeaders :: SCR.ClientM (Headers TestHeaders Bool) getDeleteContentType :: SCR.ClientM NoContent+ getGet :<|> getDeleteEmpty :<|> getCapture@@ -142,7 +145,8 @@ :<|> getRawFailure :<|> getMultiple :<|> getRespHeaders- :<|> getDeleteContentType = client api+ :<|> getDeleteContentType+ :<|> EmptyClient = client api server :: Application server = serve api (@@ -157,12 +161,12 @@ Nothing -> throwError $ ServantErr 400 "missing parameter" "" []) :<|> (\ names -> return (zipWith Person names [0..])) :<|> return- :<|> (\ _request respond -> respond $ responseLBS HTTP.ok200 [] "rawSuccess")- :<|> (\ _request respond -> respond $ responseLBS HTTP.badRequest400 [] "rawFailure")+ :<|> (Tagged $ \ _request respond -> respond $ responseLBS HTTP.ok200 [] "rawSuccess")+ :<|> (Tagged $ \ _request respond -> respond $ responseLBS HTTP.badRequest400 [] "rawFailure") :<|> (\ a b c d -> return (a, b, c, d)) :<|> (return $ addHeader 1729 $ addHeader "eg2" True) :<|> return NoContent- )+ :<|> emptyServer) type FailApi =@@ -174,9 +178,9 @@ failServer :: Application failServer = serve failApi (- (\ _request respond -> respond $ responseLBS HTTP.ok200 [] "")- :<|> (\ _capture _request respond -> respond $ responseLBS HTTP.ok200 [("content-type", "application/json")] "")- :<|> (\_request respond -> respond $ responseLBS HTTP.ok200 [("content-type", "fooooo")] "")+ (Tagged $ \ _request respond -> respond $ responseLBS HTTP.ok200 [] "")+ :<|> (\ _capture -> Tagged $ \_request respond -> respond $ responseLBS HTTP.ok200 [("content-type", "application/json")] "")+ :<|> (Tagged $ \_request respond -> respond $ responseLBS HTTP.ok200 [("content-type", "fooooo")] "") ) -- * basic auth stuff@@ -372,7 +376,7 @@ let (_ :<|> getDeleteEmpty :<|> _) = client api Left res <- runClientM getDeleteEmpty (ClientEnv manager baseUrl) case res of- FailureResponse (HTTP.Status 404 "Not Found") _ _ -> return ()+ FailureResponse _ (HTTP.Status 404 "Not Found") _ _ -> return () _ -> fail $ "expected 404 response, but got " <> show res it "reports DecodeFailure" $ \(_, baseUrl) -> do