@@ -6,6 +6,7 @@
66{-# LANGUAGE MultiParamTypeClasses #-}
77{-# LANGUAGE OverloadedStrings #-}
88{-# LANGUAGE PolyKinds #-}
9+{-# LANGUAGE RankNTypes #-}
910{-# LANGUAGE ScopedTypeVariables #-}
1011{-# LANGUAGE TypeFamilies #-}
1112{-# LANGUAGE TypeOperators #-}
@@ -35,7 +36,8 @@ import Servant.API ((:<|>) ((:<|>)), (:>),
3536 BuildFromStream (..),
3637 ByteStringParser (..),
3738 Capture', CaptureAll,
38− Description, EmptyAPI,
39+ Description, DynHeaders,
40+ EmptyAPI,
3941 FramingUnrender (..),
4042 Header', Headers (..),
4143 HttpVersion, IsSecure,
@@ -97,6 +99,12 @@ clientIn p pm = clientWithRoute pm p defaultRequest
9799class RunClient m => HasClient m api where
98100 type Client (m :: * -> *) (api :: *) :: *
99101 clientWithRoute :: Proxy m -> Proxy api -> Request -> Client m api
102+ hoistClientMonad
103+ :: Proxy m
104+ -> Proxy api
105+ -> (forall x. mon x -> mon' x)
106+ -> Client mon api
107+ -> Client mon' api
100108
101109
102110-- | A client querying function for @a ':<|>' b@ will actually hand you
@@ -118,6 +126,10 @@ instance (HasClient m a, HasClient m b) => HasClient m (a :<|> b) where
118126 clientWithRoute pm (Proxy :: Proxy a) req :<|>
119127 clientWithRoute pm (Proxy :: Proxy b) req
120128
129+ hoistClientMonad pm _ f (ca :<|> cb) =
130+ hoistClientMonad pm (Proxy :: Proxy a) f ca :<|>
131+ hoistClientMonad pm (Proxy :: Proxy b) f cb
132+
121133-- | Singleton type representing a client for an empty API.
122134data EmptyClient = EmptyClient deriving (Eq, Show, Bounded, Enum)
123135
@@ -134,6 +146,7 @@ data EmptyClient = EmptyClient deriving (Eq, Show, Bounded, Enum)
134146instance RunClient m => HasClient m EmptyAPI where
135147 type Client m EmptyAPI = EmptyClient
136148 clientWithRoute _pm Proxy _ = EmptyClient
149+ hoistClientMonad _ _ _ EmptyClient = EmptyClient
137150
138151-- | If you use a 'Capture' in one of your endpoints in your API,
139152-- the corresponding querying function will automatically take
@@ -166,6 +179,9 @@ instance (KnownSymbol capture, ToHttpApiData a, HasClient m api)
166179
167180 where p = (toUrlPiece val)
168181
182+ hoistClientMonad pm _ f cl = \a ->
183+ hoistClientMonad pm (Proxy :: Proxy api) f (cl a)
184+
169185-- | If you use a 'CaptureAll' in one of your endpoints in your API,
170186-- the corresponding querying function will automatically take an
171187-- additional argument of a list of the type specified by your
@@ -198,6 +214,9 @@ instance (KnownSymbol capture, ToHttpApiData a, HasClient m sublayout)
198214
199215 where ps = map (toUrlPiece) vals
200216
217+ hoistClientMonad pm _ f cl = \as ->
218+ hoistClientMonad pm (Proxy :: Proxy sublayout) f (cl as)
219+
201220instance OVERLAPPABLE_
202221 -- Note [Non-Empty Content Types]
203222 ( RunClient m, MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts)
@@ -213,6 +232,8 @@ instance OVERLAPPABLE_
213232 accept = contentTypes (Proxy :: Proxy ct)
214233 method = reflectMethod (Proxy :: Proxy method)
215234
235+ hoistClientMonad _ _ f ma = f ma
236+
216237instance OVERLAPPING_
217238 ( RunClient m, ReflectMethod method
218239 ) => HasClient m (Verb method status cts NoContent) where
@@ -223,6 +244,8 @@ instance OVERLAPPING_
223244 return NoContent
224245 where method = reflectMethod (Proxy :: Proxy method)
225246
247+ hoistClientMonad _ _ f ma = f ma
248+
226249instance OVERLAPPING_
227250 -- Note [Non-Empty Content Types]
228251 ( RunClient m, MimeUnrender ct a, BuildHeadersTo ls
@@ -244,6 +267,8 @@ instance OVERLAPPING_
244267 where method = reflectMethod (Proxy :: Proxy method)
245268 accept = contentTypes (Proxy :: Proxy ct)
246269
270+ hoistClientMonad _ _ f ma = f ma
271+
247272instance OVERLAPPING_
248273 ( RunClient m, BuildHeadersTo ls, ReflectMethod method
249274 ) => HasClient m (Verb method status cts (Headers ls NoContent)) where
@@ -256,6 +281,38 @@ instance OVERLAPPING_
256281 , getHeadersHList = buildHeadersTo . toList $ responseHeaders response
257282 }
258283
284+ hoistClientMonad _ _ f ma = f ma
285+
286+instance OVERLAPPING_
287+ ( RunClient m, MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts)
288+ ) => HasClient m (Verb method status cts' (DynHeaders a)) where
289+ type Client m (Verb method status cts' (DynHeaders a)) = m a
290+ clientWithRoute _pm Proxy req = do
291+ response <- runRequest req
292+ { requestMethod = method
293+ , requestAccept = fromList $ toList accept
294+ }
295+ case mimeUnrender (Proxy :: Proxy ct) $ responseBody response of
296+ Left err -> throwServantError $ DecodeFailure (pack err) response
297+ Right val -> return val
298+
299+ where method = reflectMethod (Proxy :: Proxy method)
300+ accept = contentTypes (Proxy :: Proxy ct)
301+
302+ hoistClientMonad _ _ f ma = f ma
303+
304+instance OVERLAPPING_
305+ ( RunClient m, ReflectMethod method
306+ ) => HasClient m (Verb method status cts (DynHeaders NoContent)) where
307+ type Client m (Verb method status cts (DynHeaders NoContent)) = m NoContent
308+ clientWithRoute _pm Proxy req = do
309+ response <- runRequest req { requestMethod = method }
310+ return NoContent
311+
312+ where method = reflectMethod (Proxy :: Proxy method)
313+
314+ hoistClientMonad _ _ f ma = f ma
315+
259316instance OVERLAPPABLE_
260317 ( RunClient m, MimeUnrender ct a, ReflectMethod method,
261318 FramingUnrender framing a, BuildFromStream a (f a)
@@ -304,6 +361,7 @@ instance OVERLAPPABLE_
304361 processResult (Left err, _) = Just (Left err)
305362 k go
306363
364+ hoistClientMonad _ _ f ma = f ma
307365
308366-- | If you use a 'Header' in one of your endpoints in your API,
309367-- the corresponding querying function will automatically take
@@ -345,6 +403,9 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient m api, SBoolI (FoldRequire
345403 add :: a -> Request
346404 add value = addHeader hname value req
347405
406+ hoistClientMonad pm _ f cl = \arg ->
407+ hoistClientMonad pm (Proxy :: Proxy api) f (cl arg)
408+
348409-- | Using a 'HttpVersion' combinator in your API doesn't affect the client
349410-- functions.
350411instance HasClient m api
@@ -356,18 +417,24 @@ instance HasClient m api
356417 clientWithRoute pm Proxy =
357418 clientWithRoute pm (Proxy :: Proxy api)
358419
420+ hoistClientMonad pm _ f cl = hoistClientMonad pm (Proxy :: Proxy api) f cl
421+
359422-- | Ignore @'Summary'@ in client functions.
360423instance HasClient m api => HasClient m (Summary desc :> api) where
361424 type Client m (Summary desc :> api) = Client m api
362425
363426 clientWithRoute pm _ = clientWithRoute pm (Proxy :: Proxy api)
364427
428+ hoistClientMonad pm _ f cl = hoistClientMonad pm (Proxy :: Proxy api) f cl
429+
365430-- | Ignore @'Description'@ in client functions.
366431instance HasClient m api => HasClient m (Description desc :> api) where
367432 type Client m (Description desc :> api) = Client m api
368433
369434 clientWithRoute pm _ = clientWithRoute pm (Proxy :: Proxy api)
370435
436+ hoistClientMonad pm _ f cl = hoistClientMonad pm (Proxy :: Proxy api) f cl
437+
371438-- | If you use a 'QueryParam' in one of your endpoints in your API,
372439-- the corresponding querying function will automatically take
373440-- an additional argument of the type specified by your 'QueryParam',
@@ -410,6 +477,9 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient m api, SBoolI (FoldRequire
410477 pname :: Text
411478 pname = pack $ symbolVal (Proxy :: Proxy sym)
412479
480+ hoistClientMonad pm _ f cl = \arg ->
481+ hoistClientMonad pm (Proxy :: Proxy api) f (cl arg)
482+
413483-- | If you use a 'QueryParams' in one of your endpoints in your API,
414484-- the corresponding querying function will automatically take
415485-- an additional argument, a list of values of the type specified
@@ -453,6 +523,9 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient m api)
453523 where pname = pack $ symbolVal (Proxy :: Proxy sym)
454524 paramlist' = map (Just . toQueryParam) paramlist
455525
526+ hoistClientMonad pm _ f cl = \as ->
527+ hoistClientMonad pm (Proxy :: Proxy api) f (cl as)
528+
456529-- | If you use a 'QueryFlag' in one of your endpoints in your API,
457530-- the corresponding querying function will automatically take
458531-- an additional 'Bool' argument.
@@ -489,6 +562,8 @@ instance (KnownSymbol sym, HasClient m api)
489562
490563 where paramname = pack $ symbolVal (Proxy :: Proxy sym)
491564
565+ hoistClientMonad pm _ f cl = \b ->
566+ hoistClientMonad pm (Proxy :: Proxy api) f (cl b)
492567
493568-- | Pick a 'Method' and specify where the server you want to query is. You get
494569-- back the full `Response`.
@@ -500,6 +575,8 @@ instance RunClient m => HasClient m Raw where
500575 clientWithRoute _pm Proxy req httpMethod = do
501576 runRequest req { requestMethod = httpMethod }
502577
578+ hoistClientMonad _ _ f cl = \meth -> f (cl meth)
579+
503580-- | If you use a 'ReqBody' in one of your endpoints in your API,
504581-- the corresponding querying function will automatically take
505582-- an additional argument of the type specified by your 'ReqBody'.
@@ -533,6 +610,9 @@ instance (MimeRender ct a, HasClient m api)
533610 req
534611 )
535612
613+ hoistClientMonad pm _ f cl = \a ->
614+ hoistClientMonad pm (Proxy :: Proxy api) f (cl a)
615+
536616-- | Make the querying function append @path@ to the request path.
537617instance (KnownSymbol path, HasClient m api) => HasClient m (path :> api) where
538618 type Client m (path :> api) = Client m api
@@ -543,30 +623,40 @@ instance (KnownSymbol path, HasClient m api) => HasClient m (path :> api) where
543623
544624 where p = pack $ symbolVal (Proxy :: Proxy path)
545625
626+ hoistClientMonad pm _ f cl = hoistClientMonad pm (Proxy :: Proxy api) f cl
627+
546628instance HasClient m api => HasClient m (Vault :> api) where
547629 type Client m (Vault :> api) = Client m api
548630
549631 clientWithRoute pm Proxy req =
550632 clientWithRoute pm (Proxy :: Proxy api) req
551633
634+ hoistClientMonad pm _ f cl = hoistClientMonad pm (Proxy :: Proxy api) f cl
635+
552636instance HasClient m api => HasClient m (RemoteHost :> api) where
553637 type Client m (RemoteHost :> api) = Client m api
554638
555639 clientWithRoute pm Proxy req =
556640 clientWithRoute pm (Proxy :: Proxy api) req
557641
642+ hoistClientMonad pm _ f cl = hoistClientMonad pm (Proxy :: Proxy api) f cl
643+
558644instance HasClient m api => HasClient m (IsSecure :> api) where
559645 type Client m (IsSecure :> api) = Client m api
560646
561647 clientWithRoute pm Proxy req =
562648 clientWithRoute pm (Proxy :: Proxy api) req
563649
650+ hoistClientMonad pm _ f cl = hoistClientMonad pm (Proxy :: Proxy api) f cl
651+
564652instance HasClient m subapi =>
565653 HasClient m (WithNamedContext name context subapi) where
566654
567655 type Client m (WithNamedContext name context subapi) = Client m subapi
568656 clientWithRoute pm Proxy = clientWithRoute pm (Proxy :: Proxy subapi)
569657
658+ hoistClientMonad pm _ f cl = hoistClientMonad pm (Proxy :: Proxy subapi) f cl
659+
570660instance ( HasClient m api
571661 ) => HasClient m (AuthProtect tag :> api) where
572662 type Client m (AuthProtect tag :> api)
@@ -575,6 +665,9 @@ instance ( HasClient m api
575665 clientWithRoute pm Proxy req (AuthenticatedRequest (val,func)) =
576666 clientWithRoute pm (Proxy :: Proxy api) (func val req)
577667
668+ hoistClientMonad pm _ f cl = \authreq ->
669+ hoistClientMonad pm (Proxy :: Proxy api) f (cl authreq)
670+
578671-- * Basic Authentication
579672
580673instance HasClient m api => HasClient m (BasicAuth realm usr :> api) where
@@ -583,6 +676,9 @@ instance HasClient m api => HasClient m (BasicAuth realm usr :> api) where
583676 clientWithRoute pm Proxy req val =
584677 clientWithRoute pm (Proxy :: Proxy api) (basicAuthReq val req)
585678
679+ hoistClientMonad pm _ f cl = \bauth ->
680+ hoistClientMonad pm (Proxy :: Proxy api) f (cl bauth)
681+
586682
587683{- Note [Non-Empty Content Types]
588684~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~