servant-client 0.2.1 → 0.2.2
raw patch · 3 files changed
+134/−9 lines, 3 filesdep +http-client-tlsdep ~QuickCheckdep ~servantdep ~servant-servernew-uploader
Dependencies added: http-client-tls
Dependency ranges changed: QuickCheck, servant, servant-server
Files
- servant-client.cabal +5/−4
- src/Servant/Client.hs +119/−3
- src/Servant/Common/Req.hs +10/−2
servant-client.cabal view
@@ -1,5 +1,5 @@ name: servant-client-version: 0.2.1+version: 0.2.2 synopsis: automatical derivation of querying functions for servant webservices description: This library lets you derive automatically Haskell functions that@@ -44,10 +44,11 @@ , either , exceptions , http-client+ , http-client-tls , http-types , network-uri >= 2.6 , safe- , servant >= 0.2.1+ , servant >= 0.2.2 && < 0.3 , string-conversions , text , transformers@@ -71,9 +72,9 @@ , hspec == 2.* , http-types , network >= 2.6- , QuickCheck+ , QuickCheck >= 2.7 , servant >= 0.2.1 , servant-client- , servant-server >= 0.2.1+ , servant-server >= 0.2.1 && < 0.3 , wai , warp
src/Servant/Client.hs view
@@ -13,6 +13,7 @@ , module Servant.Common.BaseUrl ) where +import Control.Monad import Control.Monad.Trans.Either import Data.Aeson import Data.ByteString.Lazy (ByteString)@@ -108,7 +109,7 @@ type Client Delete = BaseUrl -> EitherT String IO () clientWithRoute Proxy req host =- performRequestJSON H.methodDelete req 204 host+ void $ performRequest H.methodDelete req (== 204) host -- | If you have a 'Get' endpoint in your API, the client -- side querying function that is created when calling 'client'@@ -210,7 +211,7 @@ -- if mparam = Nothing, we don't add it to the query string clientWithRoute Proxy req mparam = clientWithRoute (Proxy :: Proxy sublayout) $- appendToQueryString pname mparamText req+ maybe req (flip (appendToQueryString pname) req . Just) mparamText where pname = cs pname' pname' = symbolVal (Proxy :: Proxy sym)@@ -251,7 +252,7 @@ clientWithRoute Proxy req paramlist = clientWithRoute (Proxy :: Proxy sublayout) $- foldl' (\ value req' -> appendToQueryString pname req' value) req paramlist'+ foldl' (\ req' -> maybe req' (flip (appendToQueryString pname) req' . Just)) req paramlist' where pname = cs pname' pname' = symbolVal (Proxy :: Proxy sym)@@ -288,6 +289,121 @@ clientWithRoute (Proxy :: Proxy sublayout) $ if flag then appendToQueryString paramname Nothing req+ else req++ where paramname = cs $ symbolVal (Proxy :: Proxy sym)++-- | If you use a 'MatrixParam' in one of your endpoints in your API,+-- the corresponding querying function will automatically take+-- an additional argument of the type specified by your 'MatrixParam',+-- enclosed in Maybe.+--+-- If you give Nothing, nothing will be added to the query string.+--+-- If you give a non-'Nothing' value, this function will take care+-- of inserting a textual representation of this value in the query string.+--+-- You can control how values for your type are turned into+-- text by specifying a 'ToText' instance for your type.+--+-- Example:+--+-- > type MyApi = "books" :> MatrixParam "author" Text :> Get [Book]+-- >+-- > myApi :: Proxy MyApi+-- > myApi = Proxy+-- >+-- > getBooksBy :: Maybe Text -> BaseUrl -> EitherT String IO [Book]+-- > getBooksBy = client myApi+-- > -- then you can just use "getBooksBy" to query that endpoint.+-- > -- 'getBooksBy Nothing' for all books+-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov+instance (KnownSymbol sym, ToText a, HasClient sublayout)+ => HasClient (MatrixParam sym a :> sublayout) where++ type Client (MatrixParam sym a :> sublayout) =+ Maybe a -> Client sublayout++ -- if mparam = Nothing, we don't add it to the query string+ clientWithRoute Proxy req mparam =+ clientWithRoute (Proxy :: Proxy sublayout) $+ maybe req (flip (appendToMatrixParams pname . Just) req) mparamText++ where pname = symbolVal (Proxy :: Proxy sym)+ mparamText = fmap (cs . toText) mparam++-- | If you use a 'MatrixParams' in one of your endpoints in your API,+-- the corresponding querying function will automatically take an+-- additional argument, a list of values of the type specified by your+-- 'MatrixParams'.+--+-- If you give an empty list, nothing will be added to the query string.+--+-- Otherwise, this function will take care of inserting a textual+-- representation of your values in the path segment string, under the+-- same matrix string parameter name.+--+-- You can control how values for your type are turned into text by+-- specifying a 'ToText' instance for your type.+--+-- Example:+--+-- > type MyApi = "books" :> MatrixParams "authors" Text :> Get [Book]+-- >+-- > myApi :: Proxy MyApi+-- > myApi = Proxy+-- >+-- > getBooksBy :: [Text] -> BaseUrl -> EitherT String IO [Book]+-- > getBooksBy = client myApi+-- > -- then you can just use "getBooksBy" to query that endpoint.+-- > -- 'getBooksBy []' for all books+-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'+-- > -- to get all books by Asimov and Heinlein+instance (KnownSymbol sym, ToText a, HasClient sublayout)+ => HasClient (MatrixParams sym a :> sublayout) where++ type Client (MatrixParams sym a :> sublayout) =+ [a] -> Client sublayout++ clientWithRoute Proxy req paramlist =+ clientWithRoute (Proxy :: Proxy sublayout) $+ foldl' (\ req' value -> maybe req' (flip (appendToMatrixParams pname) req' . Just . cs) value) req paramlist'++ where pname = cs pname'+ pname' = symbolVal (Proxy :: Proxy sym)+ paramlist' = map (Just . toText) paramlist++-- | If you use a 'MatrixFlag' in one of your endpoints in your API,+-- the corresponding querying function will automatically take an+-- additional 'Bool' argument.+--+-- If you give 'False', nothing will be added to the path segment.+--+-- Otherwise, this function will insert a value-less matrix parameter+-- under the name associated to your 'MatrixFlag'.+--+-- Example:+--+-- > type MyApi = "books" :> MatrixFlag "published" :> Get [Book]+-- >+-- > myApi :: Proxy MyApi+-- > myApi = Proxy+-- >+-- > getBooks :: Bool -> BaseUrl -> EitherT String IO [Book]+-- > getBooks = client myApi+-- > -- then you can just use "getBooks" to query that endpoint.+-- > -- 'getBooksBy False' for all books+-- > -- 'getBooksBy True' to only get _already published_ books+instance (KnownSymbol sym, HasClient sublayout)+ => HasClient (MatrixFlag sym :> sublayout) where++ type Client (MatrixFlag sym :> sublayout) =+ Bool -> Client sublayout++ clientWithRoute Proxy req flag =+ clientWithRoute (Proxy :: Proxy sublayout) $+ if flag+ then appendToMatrixParams paramname Nothing req else req where paramname = cs $ symbolVal (Proxy :: Proxy sym)
src/Servant/Common/Req.hs view
@@ -19,6 +19,7 @@ import Data.Text import Data.Text.Encoding import Network.HTTP.Client+import Network.HTTP.Client.TLS import Network.HTTP.Types import Network.URI import Servant.Common.BaseUrl@@ -41,6 +42,13 @@ appendToPath p req = req { reqPath = reqPath req ++ "/" ++ p } +appendToMatrixParams :: String+ -> Maybe String+ -> Req+ -> Req+appendToMatrixParams pname pvalue req =+ req { reqPath = reqPath req ++ ";" ++ pname ++ maybe "" ("=" ++) pvalue }+ appendToQueryString :: Text -- ^ param name -> Maybe Text -- ^ param value -> Req@@ -77,14 +85,14 @@ setheaders r = r { requestHeaders = Prelude.map toProperHeader (headers req) } toProperHeader (name, val) =- (fromString name, encodeUtf8 val) + (fromString name, encodeUtf8 val) -- * performing requests {-# NOINLINE __manager #-} __manager :: MVar Manager-__manager = unsafePerformIO (newManager defaultManagerSettings >>= newMVar)+__manager = unsafePerformIO (newManager tlsManagerSettings >>= newMVar) __withGlobalManager :: (Manager -> IO a) -> IO a __withGlobalManager action = modifyMVar __manager $ \ manager -> do