@@ -155,6 +155,7 @@ module Network.HTTP.Req
155155 -- $query-parameters
156156 (=:),
157157 queryFlag,
158+ formToQuery,
158159 QueryParam (..),
159160
160161 -- *** Headers
@@ -272,6 +273,8 @@ import Text.URI (URI)
272273import qualified Text.URI as URI
273274import qualified Text.URI.QQ as QQ
274275import qualified Web.Authenticate.OAuth as OAuth
276+import Web.FormUrlEncoded (FromForm (..), ToForm (..))
277+import qualified Web.FormUrlEncoded as Form
275278import Web.HttpApiData (ToHttpApiData (..))
276279
277280----------------------------------------------------------------------------
@@ -1303,6 +1306,13 @@ newtype FormUrlEncodedParam = FormUrlEncodedParam [(Text, Maybe Text)]
13031306instance QueryParam FormUrlEncodedParam where
13041307 queryParam name mvalue =
13051308 FormUrlEncodedParam [(name, toQueryParam <$> mvalue)]
1309+ queryParamToList (FormUrlEncodedParam p) = p
1310+
1311+-- | Use 'formToQuery'.
1312+--
1313+-- @since 3.11.0
1314+instance FromForm FormUrlEncodedParam where
1315+ fromForm = Right . formToQuery
13061316
13071317-- | Multipart form data. Please consult the
13081318-- "Network.HTTP.Client.MultipartFormData" module for how to construct
@@ -1428,6 +1438,12 @@ instance Monoid (Option scheme) where
14281438 mempty = Option mempty Nothing
14291439 mappend = (<>)
14301440
1441+-- | Use 'formToQuery'.
1442+--
1443+-- @since 3.11.0
1444+instance FromForm (Option scheme) where
1445+ fromForm = Right . formToQuery
1446+
14311447-- | A helper to create an 'Option' that modifies only collection of query
14321448-- parameters. This helper is not a part of the public API.
14331449withQueryParams :: (Y.QueryText -> Y.QueryText) -> Option scheme
@@ -1482,6 +1498,24 @@ name =: value = queryParam name (pure value)
14821498queryFlag :: QueryParam param => Text -> param
14831499queryFlag name = queryParam name (Nothing :: Maybe ())
14841500
1501+-- | Construct query parameters from a 'ToForm' instance. This function
1502+-- produces the same query params as 'Form.urlEncodeAsFormStable'.
1503+--
1504+-- Note that 'Form.Form' doesn't have the concept of parameters with the
1505+-- empty value (i.e. what you can get by @key =: ""@). If the value is
1506+-- empty, it will be encoded as a valueless parameter (i.e. what you can get
1507+-- by @queryFlag key@).
1508+--
1509+-- @since 3.11.0
1510+formToQuery :: (QueryParam param, Monoid param, ToForm f) => f -> param
1511+formToQuery f = mconcat . fmap toParam . Form.toListStable $ toForm f
1512+ where
1513+ toParam (key, val) =
1514+ queryParam key $
1515+ if val == ""
1516+ then Nothing
1517+ else Just val
1518+
14851519-- | A type class for query-parameter-like things. The reason to have an
14861520-- overloaded 'queryParam' is to be able to use it as an 'Option' and as a
14871521-- 'FormUrlEncodedParam' when constructing form URL encoded request bodies.
@@ -1493,9 +1527,15 @@ class QueryParam param where
14931527 -- method, because they are easier to read.
14941528 queryParam :: ToHttpApiData a => Text -> Maybe a -> param
14951529
1530+ -- | Get the query parameter names and values set by 'queryParam'.
1531+ --
1532+ -- @since 3.11.0
1533+ queryParamToList :: param -> [(Text, Maybe Text)]
1534+
14961535instance QueryParam (Option scheme) where
14971536 queryParam name mvalue =
14981537 withQueryParams ((:) (name, toQueryParam <$> mvalue))
1538+ queryParamToList (Option f _) = fst $ appEndo f ([], L.defaultRequest)
14991539
15001540----------------------------------------------------------------------------
15011541-- Request—Optional parameters—Headers