req 0.3.1 → 0.4.0
raw patch · 4 files changed
+78/−11 lines, 4 filesdep +monad-controldep +transformers-base
Dependencies added: monad-control, transformers-base
Files
- CHANGELOG.md +5/−0
- Network/HTTP/Req.hs +42/−0
- httpbin-tests/Network/HTTP/ReqSpec.hs +27/−10
- req.cabal +4/−1
CHANGELOG.md view
@@ -1,3 +1,8 @@+## Req 0.4.0++* Added the `Req` monad and `runReq` function to run it. This allows to use+ `req` without defining new (orphan) instances.+ ## Req 0.3.1 * Added `basicAuthUnsafe`.
Network/HTTP/Req.hs view
@@ -95,6 +95,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-}@@ -123,6 +124,8 @@ -- $embedding-requests , MonadHttp (..) , HttpConfig (..)+ , Req+ , runReq -- * Request -- ** Method -- $method@@ -213,7 +216,10 @@ import Control.Arrow (first, second) import Control.Exception (Exception, try, handle, throwIO) import Control.Monad+import Control.Monad.Base import Control.Monad.IO.Class+import Control.Monad.Reader+import Control.Monad.Trans.Control import Control.Retry import Data.Aeson (ToJSON (..), FromJSON (..)) import Data.ByteString (ByteString)@@ -581,6 +587,42 @@ x { L.proxy = httpConfigProxy , L.redirectCount = httpConfigRedirectCount , LI.requestManagerOverride = httpConfigAltManager }++-- | A monad that allows to run 'req' in any 'IO'-enabled monad without+-- having to define new instances.+--+-- @since 0.4.0++newtype Req a = Req (ReaderT HttpConfig IO a)+ deriving ( Functor+ , Applicative+ , Monad+ , MonadIO )++instance MonadBase IO Req where+ liftBase = liftIO++instance MonadBaseControl IO Req where+ type StM Req a = a+ liftBaseWith f = Req . ReaderT $ \r -> f (runReq r)+ {-# INLINEABLE liftBaseWith #-}+ restoreM = Req . ReaderT . const . return+ {-# INLINEABLE restoreM #-}++instance MonadHttp Req where+ handleHttpException = Req . lift . throwIO+ getHttpConfig = Req ask++-- | Run a computation in the 'Req' monad with the given 'HttpConfig'. In+-- case of exceptional situation an 'HttpException' will be thrown.+--+-- @since 0.4.0++runReq :: MonadIO m+ => HttpConfig -- ^ 'HttpConfig' to use+ -> Req a -- ^ Computation to run+ -> m a+runReq config (Req m) = liftIO (runReaderT m config) ---------------------------------------------------------------------------- -- Request—Method
httpbin-tests/Network/HTTP/ReqSpec.hs view
@@ -9,8 +9,9 @@ ( spec ) where -import Control.Exception (throwIO)+import Control.Exception import Control.Monad.Reader+import Control.Monad.Trans.Control import Data.Aeson (Value (..), ToJSON (..), object, (.=)) import Data.Default.Class import Data.IORef@@ -41,17 +42,18 @@ spec :: Spec spec = do - describe "exception throwing on non 2xx-status codes" $- it "throws indeed for non-2xx" $ do- let selector :: HttpException -> Bool- selector (VanillaHttpException- (L.HttpExceptionRequest _- (L.StatusCodeException response chunk))) =- L.responseStatus response == Y.status404 && not (B.null chunk)- selector _ = False+ describe "exception throwing on non-2xx status codes" $+ it "throws indeed for non-2xx" $ req GET (httpbin /: "foo") NoReqBody ignoreResponse mempty- `shouldThrow` selector+ `shouldThrow` selector404 + describe "exception throwing on non-2xx status codes (Req monad)" $+ it "throws indeed for non-2xx" $+ asIO . runReq def $+ liftBaseWith $ \run ->+ run (req GET (httpbin /: "foo") NoReqBody ignoreResponse mempty)+ `shouldThrow` selector404+ describe "response check via httpConfigCheckResponse" $ context "if it's set to always throw" $ it "throws indeed" $@@ -401,6 +403,15 @@ NoReqBody ignoreResponse mempty responseStatusCode r `shouldBe` code +-- | Exception selector that selects only 404 “Not found” exceptions.++selector404 :: HttpException -> Bool+selector404 (VanillaHttpException+ (L.HttpExceptionRequest _+ (L.StatusCodeException response chunk))) =+ L.responseStatus response == Y.status404 && not (B.null chunk)+selector404 _ = False+ -- | Empty JSON 'Object'. emptyObject :: Value@@ -410,3 +421,9 @@ reflectJSON :: ToJSON a => a -> Text reflectJSON = T.decodeUtf8 . BL.toStrict . A.encode++-- | Clarify to the type checker that the inner computation is in the 'IO'+-- monad.++asIO :: IO a -> IO a+asIO = id
req.cabal view
@@ -1,5 +1,5 @@ name: req-version: 0.3.1+version: 0.4.0 cabal-version: >= 1.18 tested-with: GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.1 license: BSD3@@ -39,11 +39,13 @@ , http-client >= 0.5 && < 0.6 , http-client-tls >= 0.3.2 && < 0.4 , http-types >= 0.8 && < 10.0+ , monad-control >= 1.0 && < 1.1 , mtl >= 2.0 && < 3.0 , retry >= 0.7 && < 0.8 , text >= 0.2 && < 1.3 , time >= 1.2 && < 1.9 , transformers >= 0.4 && < 0.6+ , transformers-base if !impl(ghc >= 8.0) build-depends: semigroups == 0.18.* exposed-modules: Network.HTTP.Req@@ -92,6 +94,7 @@ , hspec >= 2.0 && < 3.0 , http-client >= 0.5 && < 0.6 , http-types >= 0.8 && < 10.0+ , monad-control >= 1.0 && < 1.1 , mtl >= 2.0 && < 3.0 , req , retry >= 0.7 && < 0.8