servant-client-core 0.16 → 0.17
raw patch · 6 files changed
+140/−20 lines, 6 filesdep ~servantPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: servant
API changes (from Hackage documentation)
- Servant.Client.Core.Request: instance (GHC.Show.Show path, GHC.Show.Show body) => GHC.Show.Show (Servant.Client.Core.Request.RequestF body path)
+ Servant.Client.Core.HasClient: instance forall k1 (m :: * -> *) (method :: k1). (Servant.Client.Core.RunClient.RunClient m, Servant.API.Verbs.ReflectMethod method) => Servant.Client.Core.HasClient.HasClient m (Servant.API.Verbs.NoContentVerb method)
+ Servant.Client.Core.HasClient: instance forall k1 (m :: * -> *) ct chunk (method :: k1) framing a (hs :: [*]) (status :: GHC.Types.Nat). (Servant.Client.Core.RunClient.RunStreamingClient m, Servant.API.ContentTypes.MimeUnrender ct chunk, Servant.API.Verbs.ReflectMethod method, Servant.API.Stream.FramingUnrender framing, Servant.API.Stream.FromSourceIO chunk a, Servant.API.ResponseHeaders.BuildHeadersTo hs) => Servant.Client.Core.HasClient.HasClient m (Servant.API.Stream.Stream method status framing ct (Servant.API.ResponseHeaders.Headers hs a))
+ Servant.Client.Core.Request: instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (Servant.Client.Core.Request.RequestF a b)
Files
- CHANGELOG.md +40/−1
- servant-client-core.cabal +12/−9
- src/Servant/Client/Core/HasClient.hs +43/−8
- src/Servant/Client/Core/Request.hs +25/−1
- src/Servant/Client/Generic.hs +1/−1
- test/Servant/Client/Core/RequestSpec.hs +19/−0
CHANGELOG.md view
@@ -1,6 +1,45 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client-core/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.17+----++### Significant changes++- Add NoContentVerb [#1028](https://github.com/haskell-servant/servant/issues/1028) [#1219](https://github.com/haskell-servant/servant/pull/1219) [#1228](https://github.com/haskell-servant/servant/pull/1228)++ The `NoContent` API endpoints should now use `NoContentVerb` combinator.+ The API type changes are usually of the kind++ ```diff+ - :<|> PostNoContent '[JSON] NoContent+ + :<|> PostNoContent+ ```++ i.e. one doesn't need to specify the content-type anymore. There is no content.++- `Capture` can be `Lenient` [#1155](https://github.com/haskell-servant/servant/issues/1155) [#1156](https://github.com/haskell-servant/servant/pull/1156)++ You can specify a lenient capture as++ ```haskell+ :<|> "capture-lenient" :> Capture' '[Lenient] "foo" Int :> GET+ ```++ which will make the capture always succeed. Handlers will be of the+ type `Either String CapturedType`, where `Left err` represents+ the possible parse failure.++### Other changes++- *servant-client* *servant-client-core* *servant-http-streams* Fix Verb with headers checking content type differently [#1200](https://github.com/haskell-servant/servant/issues/1200) [#1204](https://github.com/haskell-servant/servant/pull/1204)++ For `Verb`s with response `Headers`, the implementation didn't check+ for the content-type of the response. Now it does.++- *servant-client* *servant-http-streams* `HasClient` instance for `Stream` with `Headers` [#1170](https://github.com/haskell-servant/servant/issues/1170) [#1197](https://github.com/haskell-servant/servant/pull/1197)+- *servant-client* Redact the authorization header in Show and exceptions [#1238](https://github.com/haskell-servant/servant/pull/1238)+ 0.16 ---- @@ -168,7 +207,7 @@ Just like `hoistServer` allows us to change the monad in which request handlers of a web application live in, we also have `hoistClient` for changing the monad in which *client functions* live.- Read [tutorial section for more information](https://haskell-servant.readthedocs.io/en/release-0.14/tutorial/Client.html#changing-the-monad-the-client-functions-live-in).+ Read [tutorial section for more information](https://docs.servant.dev/en/release-0.14/tutorial/Client.html#changing-the-monad-the-client-functions-live-in). ([#936](https://github.com/haskell-servant/servant/pull/936)) iF you have own combinators, you'll need to define a new method of
servant-client-core.cabal view
@@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-client-core-version: 0.16+version: 0.17 synopsis: Core functionality and class for client function generation for servant APIs category: Servant, Web@@ -8,7 +8,7 @@ This library provides backend-agnostic generation of client functions. For more information, see the README. -homepage: http://haskell-servant.readthedocs.org/+homepage: http://docs.servant.dev/ bug-reports: http://github.com/haskell-servant/servant/issues license: BSD3 license-file: LICENSE@@ -20,7 +20,9 @@ GHC ==8.0.2 || ==8.2.2 || ==8.4.4- || ==8.6.3+ || ==8.6.5+ || ==8.8.2+ , GHCJS == 8.4 extra-source-files: CHANGELOG.md@@ -53,13 +55,13 @@ -- -- note: mtl lower bound is so low because of GHC-7.8 build-depends:- base >= 4.9 && < 4.13+ base >= 4.9 && < 4.14 , bytestring >= 0.10.8.1 && < 0.11 , containers >= 0.5.7.1 && < 0.7 , deepseq >= 1.4.2.0 && < 1.5 , text >= 1.2.3.0 && < 1.3 , transformers >= 0.5.2.0 && < 0.6- , template-haskell >= 2.11.1.0 && < 2.15+ , template-haskell >= 2.11.1.0 && < 2.16 if !impl(ghc >= 8.2) build-depends:@@ -67,17 +69,17 @@ -- Servant dependencies build-depends:- servant >= 0.16 && <0.17+ servant >= 0.17 && <0.18 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. build-depends: aeson >= 1.4.1.0 && < 1.5- , base-compat >= 0.10.5 && < 0.11+ , base-compat >= 0.10.5 && < 0.12 , base64-bytestring >= 1.0.0.1 && < 1.1 , exceptions >= 0.10.0 && < 0.11 , free >= 5.1 && < 5.2- , http-media >= 0.7.1.3 && < 0.8+ , http-media >= 0.7.1.3 && < 0.9 , http-types >= 0.12.2 && < 0.13 , network-uri >= 2.6.1.0 && < 2.7 , safe >= 0.3.17 && < 0.4@@ -94,6 +96,7 @@ main-is: Spec.hs other-modules: Servant.Client.Core.Internal.BaseUrlSpec+ Servant.Client.Core.RequestSpec -- Dependencies inherited from the library. No need to specify bounds. build-depends:@@ -105,7 +108,7 @@ build-depends: deepseq >= 1.4.2.0 && < 1.5 , hspec >= 2.6.0 && < 2.8- , QuickCheck >= 2.12.6.1 && < 2.13+ , QuickCheck >= 2.12.6.1 && < 2.14 build-tool-depends: hspec-discover:hspec-discover >= 2.6.0 && <2.8
src/Servant/Client/Core/HasClient.hs view
@@ -50,8 +50,9 @@ MimeUnrender (mimeUnrender), NoContent (NoContent), QueryFlag, QueryParam', QueryParams, Raw, ReflectMethod (..), RemoteHost, ReqBody', SBoolI, Stream, StreamBody', Summary, ToHttpApiData,- ToSourceIO (..), Vault, Verb, WithNamedContext, contentType,- getHeadersHList, getResponse, toQueryParam, toUrlPiece)+ ToSourceIO (..), Vault, Verb, NoContentVerb, WithNamedContext,+ contentType, getHeadersHList, getResponse, toQueryParam,+ toUrlPiece) import Servant.API.ContentTypes (contentTypes) import Servant.API.Modifiers@@ -241,6 +242,17 @@ hoistClientMonad _ _ f ma = f ma +instance (RunClient m, ReflectMethod method) =>+ HasClient m (NoContentVerb method) where+ type Client m (NoContentVerb method)+ = m NoContent+ clientWithRoute _pm Proxy req = do+ _response <- runRequest req { requestMethod = method }+ return NoContent+ where method = reflectMethod (Proxy :: Proxy method)++ hoistClientMonad _ _ f ma = f ma+ instance {-# OVERLAPPING #-} -- Note [Non-Empty Content Types] ( RunClient m, MimeUnrender ct a, BuildHeadersTo ls@@ -253,12 +265,10 @@ { requestMethod = method , requestAccept = fromList $ toList accept }- case mimeUnrender (Proxy :: Proxy ct) $ responseBody response of- Left err -> throwClientError $ DecodeFailure (pack err) response- Right val -> return $ Headers- { getResponse = val- , getHeadersHList = buildHeadersTo . toList $ responseHeaders response- }+ val <- response `decodedAs` (Proxy :: Proxy ct)+ return $ Headers { getResponse = val+ , getHeadersHList = buildHeadersTo . toList $ responseHeaders response+ } where method = reflectMethod (Proxy :: Proxy method) accept = contentTypes (Proxy :: Proxy ct) @@ -291,6 +301,31 @@ let mimeUnrender' = mimeUnrender (Proxy :: Proxy ct) :: BL.ByteString -> Either String chunk framingUnrender' = framingUnrender (Proxy :: Proxy framing) mimeUnrender' return $ fromSourceIO $ framingUnrender' $ responseBody gres+ where+ req' = req+ { requestAccept = fromList [contentType (Proxy :: Proxy ct)]+ , requestMethod = reflectMethod (Proxy :: Proxy method)+ }++instance {-# OVERLAPPING #-}+ ( RunStreamingClient m, MimeUnrender ct chunk, ReflectMethod method,+ FramingUnrender framing, FromSourceIO chunk a,+ BuildHeadersTo hs+ ) => HasClient m (Stream method status framing ct (Headers hs a)) where++ type Client m (Stream method status framing ct (Headers hs a)) = m (Headers hs a)++ hoistClientMonad _ _ f ma = f ma++ clientWithRoute _pm Proxy req = withStreamingRequest req' $ \gres -> do+ let mimeUnrender' = mimeUnrender (Proxy :: Proxy ct) :: BL.ByteString -> Either String chunk+ framingUnrender' = framingUnrender (Proxy :: Proxy framing) mimeUnrender'+ val = fromSourceIO $ framingUnrender' $ responseBody gres+ return $ Headers+ { getResponse = val+ , getHeadersHList = buildHeadersTo . toList $ responseHeaders gres+ }+ where req' = req { requestAccept = fromList [contentType (Proxy :: Proxy ct)]
src/Servant/Client/Core/Request.hs view
@@ -64,8 +64,32 @@ , requestHeaders :: Seq.Seq Header , requestHttpVersion :: HttpVersion , requestMethod :: Method- } deriving (Generic, Typeable, Eq, Show, Functor, Foldable, Traversable)+ } deriving (Generic, Typeable, Eq, Functor, Foldable, Traversable) +instance (Show a, Show b) =>+ Show (Servant.Client.Core.Request.RequestF a b) where+ showsPrec p req+ = showParen+ (p >= 11)+ ( showString "Request {requestPath = "+ . showsPrec 0 (requestPath req)+ . showString ", requestQueryString = "+ . showsPrec 0 (requestQueryString req)+ . showString ", requestBody = "+ . showsPrec 0 (requestBody req)+ . showString ", requestAccept = "+ . showsPrec 0 (requestAccept req)+ . showString ", requestHeaders = "+ . showsPrec 0 (redactSensitiveHeader <$> requestHeaders req))+ . showString ", requestHttpVersion = "+ . showsPrec 0 (requestHttpVersion req)+ . showString ", requestMethod = "+ . showsPrec 0 (requestMethod req)+ . showString "}"+ where+ redactSensitiveHeader :: Header -> Header+ redactSensitiveHeader ("Authorization", _) = ("Authorization", "<REDACTED>")+ redactSensitiveHeader h = h instance Bifunctor RequestF where bimap = bimapDefault instance Bifoldable RequestF where bifoldMap = bifoldMapDefault instance Bitraversable RequestF where
src/Servant/Client/Generic.hs view
@@ -16,7 +16,7 @@ import Servant.API.Generic import Servant.Client.Core --- | A type that specifies that an API reocrd contains a client implementation.+-- | A type that specifies that an API record contains a client implementation. data AsClientT (m :: * -> *) instance GenericMode (AsClientT m) where type AsClientT m :- api = Client m api
+ test/Servant/Client/Core/RequestSpec.hs view
@@ -0,0 +1,19 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE OverloadedStrings #-}+module Servant.Client.Core.RequestSpec (spec) where+++import Prelude ()+import Prelude.Compat+import Control.Monad+import Data.List (isInfixOf)+import Servant.Client.Core.Request+import Test.Hspec++spec :: Spec+spec = do+ describe "Request" $ do+ describe "show" $ do+ it "redacts the authorization header" $ do+ let request = void $ defaultRequest { requestHeaders = pure ("authorization", "secret") }+ isInfixOf "secret" (show request) `shouldBe` False