req 3.10.0 → 3.11.0
raw patch · 5 files changed
+107/−30 lines, 5 files
Files
- CHANGELOG.md +7/−0
- Network/HTTP/Req.hs +41/−1
- httpbin-tests/Network/HTTP/ReqSpec.hs +13/−16
- pure-tests/Network/HTTP/ReqSpec.hs +36/−4
- req.cabal +10/−9
CHANGELOG.md view
@@ -1,3 +1,10 @@+## Req 3.11.0++* Add the `queryParamToList` method to the `QueryParam` type class.+* Add the `formToQuery` function. [Issue 126](https://github.com/mrkkrp/req/issues/126).+* Add `FromForm` instances (in the `Web.FormUrlEncoded` module) to the+ `Option` and `FormUrlEncodedParam` types.+ ## Req 3.10.0 * Add `MonadHttp` instances for `transformers` types.
Network/HTTP/Req.hs view
@@ -155,6 +155,7 @@ -- $query-parameters (=:), queryFlag,+ formToQuery, QueryParam (..), -- *** Headers@@ -251,7 +252,7 @@ import qualified Data.List.NonEmpty as NE import Data.Maybe (fromMaybe) import Data.Proxy-import Data.Semigroup hiding (Option, option)+import Data.Semigroup (Endo (..)) import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Encoding as T@@ -272,6 +273,8 @@ import qualified Text.URI as URI import qualified Text.URI.QQ as QQ import qualified Web.Authenticate.OAuth as OAuth+import Web.FormUrlEncoded (FromForm (..), ToForm (..))+import qualified Web.FormUrlEncoded as Form import Web.HttpApiData (ToHttpApiData (..)) ----------------------------------------------------------------------------@@ -1303,7 +1306,14 @@ instance QueryParam FormUrlEncodedParam where queryParam name mvalue = FormUrlEncodedParam [(name, toQueryParam <$> mvalue)]+ queryParamToList (FormUrlEncodedParam p) = p +-- | Use 'formToQuery'.+--+-- @since 3.11.0+instance FromForm FormUrlEncodedParam where+ fromForm = Right . formToQuery+ -- | Multipart form data. Please consult the -- "Network.HTTP.Client.MultipartFormData" module for how to construct -- parts, then use 'reqBodyMultipart' to create actual request body from the@@ -1428,6 +1438,12 @@ mempty = Option mempty Nothing mappend = (<>) +-- | Use 'formToQuery'.+--+-- @since 3.11.0+instance FromForm (Option scheme) where+ fromForm = Right . formToQuery+ -- | A helper to create an 'Option' that modifies only collection of query -- parameters. This helper is not a part of the public API. withQueryParams :: (Y.QueryText -> Y.QueryText) -> Option scheme@@ -1482,6 +1498,24 @@ queryFlag :: QueryParam param => Text -> param queryFlag name = queryParam name (Nothing :: Maybe ()) +-- | Construct query parameters from a 'ToForm' instance. This function+-- produces the same query params as 'Form.urlEncodeAsFormStable'.+--+-- Note that 'Form.Form' doesn't have the concept of parameters with the+-- empty value (i.e. what you can get by @key =: ""@). If the value is+-- empty, it will be encoded as a valueless parameter (i.e. what you can get+-- by @queryFlag key@).+--+-- @since 3.11.0+formToQuery :: (QueryParam param, Monoid param, ToForm f) => f -> param+formToQuery f = mconcat . fmap toParam . Form.toListStable $ toForm f+ where+ toParam (key, val) =+ queryParam key $+ if val == ""+ then Nothing+ else Just val+ -- | A type class for query-parameter-like things. The reason to have an -- overloaded 'queryParam' is to be able to use it as an 'Option' and as a -- 'FormUrlEncodedParam' when constructing form URL encoded request bodies.@@ -1493,9 +1527,15 @@ -- method, because they are easier to read. queryParam :: ToHttpApiData a => Text -> Maybe a -> param + -- | Get the query parameter names and values set by 'queryParam'.+ --+ -- @since 3.11.0+ queryParamToList :: param -> [(Text, Maybe Text)]+ instance QueryParam (Option scheme) where queryParam name mvalue = withQueryParams ((:) (name, toQueryParam <$> mvalue))+ queryParamToList (Option f _) = fst $ appEndo f ([], L.defaultRequest) ---------------------------------------------------------------------------- -- Request—Optional parameters—Headers
httpbin-tests/Network/HTTP/ReqSpec.hs view
@@ -16,6 +16,7 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import Data.Functor.Identity (runIdentity)+import Data.Maybe (fromJust) import Data.Proxy import Data.Text (Text) import qualified Data.Text as T@@ -135,7 +136,7 @@ LM.partBS "bar" "bar data!" ] r <- req POST (httpbin /: "post") body jsonResponse mempty- let Just contentType = getRequestContentType body+ let contentType = fromJust (getRequestContentType body) (stripFunnyHeaders . stripOrigin) (responseBody r) `shouldBe` object [ "args" .= emptyObject,@@ -260,21 +261,17 @@ -- TODO /response-headers -- TODO /redirect - -- FIXME Redirects test is temporarily disabled due to- --- -- https://github.com/postmanlabs/httpbin/issues/617-- -- describe "redirects" $- -- it "follows redirects" $ do- -- r <-- -- req- -- GET- -- (httpbin /: "redirect-to")- -- NoReqBody- -- ignoreResponse- -- ("url" =: ("https://httpbin.org" :: Text))- -- responseStatusCode r `shouldBe` 200- -- responseStatusMessage r `shouldBe` "OK"+ describe "redirects" $+ it "follows redirects" $ do+ r <-+ req+ GET+ (httpbin /: "redirect-to")+ NoReqBody+ ignoreResponse+ ("url" =: ("https://httpbin.org" :: Text))+ responseStatusCode r `shouldBe` 200+ responseStatusMessage r `shouldBe` "OK" -- TODO /relative-redicet -- TODO /absolute-redirect
pure-tests/Network/HTTP/ReqSpec.hs view
@@ -33,6 +33,7 @@ import qualified Data.Text.Encoding as T import Data.Time import Data.Typeable (Typeable, eqT)+import GHC.Exts (IsList (..)) import GHC.Generics import qualified Language.Haskell.TH as TH import qualified Language.Haskell.TH.Quote as TH@@ -46,6 +47,7 @@ import Text.URI (URI) import qualified Text.URI as URI import qualified Text.URI.QQ as QQ+import qualified Web.FormUrlEncoded as F spec :: Spec spec = do@@ -159,11 +161,13 @@ let uriHttp = uri' {URI.uriScheme = Just [QQ.scheme|http|]} uriHttps = uri' {URI.uriScheme = Just [QQ.scheme|https|]} requestHttp <-- let Left (url', options) = fromJust (useURI uriHttp)- in req_ GET url' NoReqBody options+ case fromJust (useURI uriHttp) of+ Left (url', options) -> req_ GET url' NoReqBody options+ _ -> error "(useURI uriHttp) should have returned Left" requestHttps <-- let Right (url', options) = fromJust (useURI uriHttps)- in req_ GET url' NoReqBody options+ case fromJust (useURI uriHttps) of+ Right (url', options) -> req_ GET url' NoReqBody options+ _ -> error "(useURI uriHttps) should have returned Right" L.host requestHttp `shouldBe` uriHost uriHttp L.host requestHttps `shouldBe` uriHost uriHttps L.port requestHttp `shouldBe` uriPort 80 uriHttp@@ -233,6 +237,19 @@ L.RequestBodyLBS x -> x `shouldBe` renderQuery params _ -> expectationFailure "Wrong request body constructor." + describe "query params" $ do+ describe "FormUrlEncodedParam" $ do+ describe "formToQuery" $ do+ it "should produce the same parameters as F.urlEncodeFormStable" $+ property $ \form -> do+ request <- req_ POST url (ReqBodyUrlEnc $ formToQuery form) mempty+ case L.requestBody request of+ L.RequestBodyLBS x -> x `shouldBe` F.urlEncodeFormStable form+ _ -> expectationFailure "Wrong request body constructor"+ specParamToList (Proxy :: Proxy FormUrlEncodedParam)+ describe "Option" $ do+ specParamToList (Proxy :: Proxy (Option 'Http))+ describe "optional parameters" $ do describe "header" $ do it "sets specified header value" $@@ -486,6 +503,9 @@ instance Arbitrary DiffTime where arbitrary = secondsToDiffTime <$> arbitrary +instance Arbitrary F.Form where+ arbitrary = (F.Form . fromList) <$> arbitrary+ ---------------------------------------------------------------------------- -- Helper types @@ -598,3 +618,15 @@ basicProxyAuthHeader username password = fromJust . lookup Y.hProxyAuthorization . L.requestHeaders $ L.applyBasicProxyAuth username password L.defaultRequest++-- | Spec about 'paramToList' for the type @p@.+specParamToList :: (QueryParam p, Monoid p) => Proxy p -> Spec+specParamToList typeProxy = do+ describe "paramToList" $ do+ it "should reproduce the parameters given by queryParam" $+ property $ \(QueryParams params) -> do+ let queryParam0 =+ (mconcat $ fmap (uncurry queryParam) params)+ `asProxyTypeOf` typeProxy+ got = queryParamToList queryParam0+ got `shouldBe` params
req.cabal view
@@ -1,11 +1,11 @@-cabal-version: 1.18+cabal-version: 2.4 name: req-version: 3.10.0-license: BSD3+version: 3.11.0+license: BSD-3-Clause license-file: LICENSE.md maintainer: Mark Karpov <markkarpov92@gmail.com> author: Mark Karpov <markkarpov92@gmail.com>-tested-with: ghc ==8.8.4 ghc ==8.10.5 ghc ==9.0.1+tested-with: ghc ==8.10.7 ghc ==9.0.2 ghc ==9.2.1 homepage: https://github.com/mrkkrp/req bug-reports: https://github.com/mrkkrp/req/issues synopsis: HTTP client library@@ -49,8 +49,8 @@ monad-control >=1.0 && <1.1, mtl >=2.0 && <3.0, retry >=0.8 && <0.10,- template-haskell >=2.14 && <2.18,- text >=0.2 && <1.3,+ template-haskell >=2.14 && <2.19,+ text >=0.2 && <2.1, time >=1.2 && <1.13, transformers >=0.5.3.0 && <0.6, transformers-base,@@ -83,14 +83,15 @@ case-insensitive >=0.2 && <1.3, hspec >=2.0 && <3.0, hspec-core >=2.0 && <3.0,+ http-api-data >=0.2 && <0.5, http-client >=0.7 && <0.8, http-types >=0.8 && <10.0, modern-uri >=0.3 && <0.4, mtl >=2.0 && <3.0, req, retry >=0.8 && <0.10,- template-haskell >=2.14 && <2.18,- text >=0.2 && <1.3,+ template-haskell >=2.14 && <2.19,+ text >=0.2 && <2.1, time >=1.2 && <1.13 if flag(dev)@@ -117,7 +118,7 @@ monad-control >=1.0 && <1.1, mtl >=2.0 && <3.0, req,- text >=0.2 && <1.3+ text >=0.2 && <2.1 if flag(dev) ghc-options: -O0 -Wall -Werror