on master help sign in
expose client runner as a typeclass with base implementation #798
5e2c48bArnaud Bailly
servant-client/servant-client.cabal+1
@@ -30,6 +30,7 @@ source-repository head
30library
30library
31 exposed-modules:
31 exposed-modules:
32 Servant.Client
32 Servant.Client
33+ Servant.Client.Class
33 Servant.Client.HttpClient
34 Servant.Client.HttpClient
34 Servant.Client.Generic
35 Servant.Client.Generic
35 Servant.Client.Experimental.Auth
36 Servant.Client.Experimental.Auth
servant-client/src/Servant/Client.hs+103−96
@@ -1,15 +1,16 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE CPP #-}
2+{-# LANGUAGE CPP #-}
3−{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleContexts #-}
5−{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE FlexibleInstances #-}
6−{-# LANGUAGE InstanceSigs #-}
6+{-# LANGUAGE InstanceSigs #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE OverloadedStrings #-}
8+{-# LANGUAGE OverloadedStrings #-}
8−{-# LANGUAGE PolyKinds #-}
9+{-# LANGUAGE PolyKinds #-}
9−{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
10−{-# LANGUAGE TypeFamilies #-}
11+{-# LANGUAGE TypeFamilies #-}
11−{-# LANGUAGE TypeOperators #-}
12+{-# LANGUAGE TypeOperators #-}
12−{-# LANGUAGE UndecidableInstances #-}
13+{-# LANGUAGE UndecidableInstances #-}
13
14
14#include "overlapping-compat.h"
15#include "overlapping-compat.h"
15-- | This module provides 'client' which can automatically generate
16-- | This module provides 'client' which can automatically generate
@@ -62,15 +63,15 @@ import Servant.Common.Req
62-- > getAllBooks :: ClientM [Book]
63-- > getAllBooks :: ClientM [Book]
63-- > postNewBook :: Book -> ClientM Book
64-- > postNewBook :: Book -> ClientM Book
64-- > (getAllBooks :<|> postNewBook) = client myApi
65-- > (getAllBooks :<|> postNewBook) = client myApi
65−client :: HasClient api => Proxy api -> Client m api
66+client :: HasClient m api => Proxy m -> Proxy api -> Client m api
66−client p = clientWithRoute p defReq
67+client pm p = clientWithRoute pm p defReq
67
68
68-- | This class lets us define how each API combinator
69-- | This class lets us define how each API combinator
69-- influences the creation of an HTTP request. It's mostly
70-- influences the creation of an HTTP request. It's mostly
70-- an internal class, you can just use 'client'.
71-- an internal class, you can just use 'client'.
71−class HasClient api where
72+class HasClient m api where
72 type Client (m :: * -> *) (api :: *) :: *
73 type Client (m :: * -> *) (api :: *) :: *
73− clientWithRoute :: Proxy api -> Req -> Client m api
74+ clientWithRoute :: Proxy m -> Proxy api -> Req -> Client m api
74
75
75
76
76-- | A client querying function for @a ':<|>' b@ will actually hand you
77-- | A client querying function for @a ':<|>' b@ will actually hand you
@@ -86,11 +87,11 @@ class HasClient api where
86-- > getAllBooks :: ClientM [Book]
87-- > getAllBooks :: ClientM [Book]
87-- > postNewBook :: Book -> ClientM Book
88-- > postNewBook :: Book -> ClientM Book
88-- > (getAllBooks :<|> postNewBook) = client myApi
89-- > (getAllBooks :<|> postNewBook) = client myApi
89−instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
90+instance (HasClient m a, HasClient m b) => HasClient m (a :<|> b) where
90 type Client m (a :<|> b) = Client m a :<|> Client m b
91 type Client m (a :<|> b) = Client m a :<|> Client m b
91− clientWithRoute Proxy req =
92+ clientWithRoute pm Proxy req =
92− clientWithRoute (Proxy :: Proxy a) req :<|>
93+ clientWithRoute pm (Proxy :: Proxy a) req :<|>
93− clientWithRoute (Proxy :: Proxy b) req
94+ clientWithRoute pm (Proxy :: Proxy b) req
94
95
95-- | Singleton type representing a client for an empty API.
96-- | Singleton type representing a client for an empty API.
96data EmptyClient = EmptyClient deriving (Eq, Show, Bounded, Enum)
97data EmptyClient = EmptyClient deriving (Eq, Show, Bounded, Enum)
@@ -105,9 +106,9 @@ data EmptyClient = EmptyClient deriving (Eq, Show, Bounded, Enum)
105-- >
106-- >
106-- > getAllBooks :: ClientM [Book]
107-- > getAllBooks :: ClientM [Book]
107-- > (getAllBooks :<|> EmptyClient) = client myApi
108-- > (getAllBooks :<|> EmptyClient) = client myApi
108−instance HasClient EmptyAPI where
109+instance HasClient m EmptyAPI where
109 type Client m EmptyAPI = EmptyClient
110 type Client m EmptyAPI = EmptyClient
110− clientWithRoute Proxy _ = EmptyClient
111+ clientWithRoute pm Proxy _ = EmptyClient
111
112
112-- | If you use a 'Capture' in one of your endpoints in your API,
113-- | If you use a 'Capture' in one of your endpoints in your API,
113-- the corresponding querying function will automatically take
114-- the corresponding querying function will automatically take
@@ -128,14 +129,14 @@ instance HasClient EmptyAPI where
128-- > getBook :: Text -> ClientM Book
129-- > getBook :: Text -> ClientM Book
129-- > getBook = client myApi
130-- > getBook = client myApi
130-- > -- then you can just use "getBook" to query that endpoint
131-- > -- then you can just use "getBook" to query that endpoint
131−instance (KnownSymbol capture, ToHttpApiData a, HasClient api)
132+instance (KnownSymbol capture, ToHttpApiData a, HasClient m api)
132− => HasClient (Capture capture a :> api) where
133+ => HasClient m (Capture capture a :> api) where
133
134
134 type Client m (Capture capture a :> api) =
135 type Client m (Capture capture a :> api) =
135 a -> Client m api
136 a -> Client m api
136
137
137− clientWithRoute Proxy req val =
138+ clientWithRoute pm Proxy req val =
138− clientWithRoute (Proxy :: Proxy api)
139+ clientWithRoute pm (Proxy :: Proxy api)
139 (appendToPath p req)
140 (appendToPath p req)
140
141
141 where p = unpack (toUrlPiece val)
142 where p = unpack (toUrlPiece val)
@@ -160,14 +161,14 @@ instance (KnownSymbol capture, ToHttpApiData a, HasClient api)
160-- > getSourceFile :: [Text] -> ClientM SourceFile
161-- > getSourceFile :: [Text] -> ClientM SourceFile
161-- > getSourceFile = client myApi
162-- > getSourceFile = client myApi
162-- > -- then you can use "getSourceFile" to query that endpoint
163-- > -- then you can use "getSourceFile" to query that endpoint
163−instance (KnownSymbol capture, ToHttpApiData a, HasClient sublayout)
164+instance (KnownSymbol capture, ToHttpApiData a, HasClient m sublayout)
164− => HasClient (CaptureAll capture a :> sublayout) where
165+ => HasClient m (CaptureAll capture a :> sublayout) where
165
166
166 type Client m (CaptureAll capture a :> sublayout) =
167 type Client m (CaptureAll capture a :> sublayout) =
167 [a] -> Client m sublayout
168 [a] -> Client m sublayout
168
169
169− clientWithRoute Proxy req vals =
170+ clientWithRoute pm Proxy req vals =
170− clientWithRoute (Proxy :: Proxy sublayout)
171+ clientWithRoute pm (Proxy :: Proxy sublayout)
171 (foldl' (flip appendToPath) req ps)
172 (foldl' (flip appendToPath) req ps)
172
173
173 where ps = map (unpack . toUrlPiece) vals
174 where ps = map (unpack . toUrlPiece) vals
@@ -175,27 +176,31 @@ instance (KnownSymbol capture, ToHttpApiData a, HasClient sublayout)
175instance OVERLAPPABLE_
176instance OVERLAPPABLE_
176 -- Note [Non-Empty Content Types]
177 -- Note [Non-Empty Content Types]
177 (RunClient m ct ([H.Header], a), MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts)
178 (RunClient m ct ([H.Header], a), MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts)
178− ) => HasClient (Verb method status cts' a) where
179+ ) => HasClient m (Verb method status cts' a) where
179 type Client m (Verb method status cts' a) = m a
180 type Client m (Verb method status cts' a) = m a
180− clientWithRoute Proxy req = do
181+ clientWithRoute pm Proxy req = do
181− snd <$> runRequest (Proxy :: Proxy ct) method req
182+ (_hdrs, a) :: ([H.Header], a) <- runRequest (Proxy :: Proxy ct) method req
183+ return a
182 where method = reflectMethod (Proxy :: Proxy method)
184 where method = reflectMethod (Proxy :: Proxy method)
183
185
184instance OVERLAPPING_
186instance OVERLAPPING_
187+ ( RunClient m NoContent [HTTP.Header]
185− (RunClient m NoContent [H.Header], ReflectMethod method) => HasClient (Verb method status cts NoContent) where
188+ , ReflectMethod method) => HasClient m (Verb method status cts NoContent) where
186 type Client m (Verb method status cts NoContent)
189 type Client m (Verb method status cts NoContent)
187 = m NoContent
190 = m NoContent
188− clientWithRoute Proxy req = do
191+ clientWithRoute pm Proxy req = do
189− runRequest (Proxy :: Proxy NoContent) method req >> return NoContent
192+ _hdrs :: [H.Header] <- runRequest (Proxy :: Proxy NoContent) method req
193+ return NoContent
190 where method = reflectMethod (Proxy :: Proxy method)
194 where method = reflectMethod (Proxy :: Proxy method)
191
195
192instance OVERLAPPING_
196instance OVERLAPPING_
193 -- Note [Non-Empty Content Types]
197 -- Note [Non-Empty Content Types]
198+ ( RunClient m ct ([H.Header], a)
194− ( RunClient m ct ([H.Header], a), MimeUnrender ct a, BuildHeadersTo ls, ReflectMethod method, cts' ~ (ct ': cts)
199+ , MimeUnrender ct a, BuildHeadersTo ls, ReflectMethod method, cts' ~ (ct ': cts)
195− ) => HasClient (Verb method status cts' (Headers ls a)) where
200+ ) => HasClient m (Verb method status cts' (Headers ls a)) where
196 type Client m (Verb method status cts' (Headers ls a))
201 type Client m (Verb method status cts' (Headers ls a))
197 = m (Headers ls a)
202 = m (Headers ls a)
198− clientWithRoute Proxy req = do
203+ clientWithRoute pm Proxy req = do
199 let method = reflectMethod (Proxy :: Proxy method)
204 let method = reflectMethod (Proxy :: Proxy method)
200 (hdrs, resp) <- runRequest (Proxy :: Proxy ct) method req
205 (hdrs, resp) <- runRequest (Proxy :: Proxy ct) method req
201 return $ Headers { getResponse = resp
206 return $ Headers { getResponse = resp
@@ -203,13 +208,14 @@ instance OVERLAPPING_
203 }
208 }
204
209
205instance OVERLAPPING_
210instance OVERLAPPING_
211+ ( RunClient m NoContent [H.Header]
206− ( RunClient m NoContent [H.Header], BuildHeadersTo ls, ReflectMethod method
212+ , BuildHeadersTo ls, ReflectMethod method
207− ) => HasClient (Verb method status cts (Headers ls NoContent)) where
213+ ) => HasClient m (Verb method status cts (Headers ls NoContent)) where
208 type Client m (Verb method status cts (Headers ls NoContent))
214 type Client m (Verb method status cts (Headers ls NoContent))
209 = m (Headers ls NoContent)
215 = m (Headers ls NoContent)
210− clientWithRoute Proxy req = do
216+ clientWithRoute pm Proxy req = do
211 let method = reflectMethod (Proxy :: Proxy method)
217 let method = reflectMethod (Proxy :: Proxy method)
212− hdrs <- runRequest method req
218+ hdrs <- runRequest (Proxy :: Proxy NoContent) method req
213 return $ Headers { getResponse = NoContent
219 return $ Headers { getResponse = NoContent
214 , getHeadersHList = buildHeadersTo hdrs
220 , getHeadersHList = buildHeadersTo hdrs
215 }
221 }
@@ -240,14 +246,14 @@ instance OVERLAPPING_
240-- > viewReferer = client myApi
246-- > viewReferer = client myApi
241-- > -- then you can just use "viewRefer" to query that endpoint
247-- > -- then you can just use "viewRefer" to query that endpoint
242-- > -- specifying Nothing or e.g Just "http://haskell.org/" as arguments
248-- > -- specifying Nothing or e.g Just "http://haskell.org/" as arguments
243−instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
249+instance (KnownSymbol sym, ToHttpApiData a, HasClient m api)
244− => HasClient (Header sym a :> api) where
250+ => HasClient m (Header sym a :> api) where
245
251
246 type Client m (Header sym a :> api) =
252 type Client m (Header sym a :> api) =
247 Maybe a -> Client m api
253 Maybe a -> Client m api
248
254
249− clientWithRoute Proxy req mval =
255+ clientWithRoute pm Proxy req mval =
250− clientWithRoute (Proxy :: Proxy api)
256+ clientWithRoute pm (Proxy :: Proxy api)
251 (maybe req
257 (maybe req
252 (\value -> Servant.Common.Req.addHeader hname value req)
258 (\value -> Servant.Common.Req.addHeader hname value req)
253 mval
259 mval
@@ -257,26 +263,26 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
257
263
258-- | Using a 'HttpVersion' combinator in your API doesn't affect the client
264-- | Using a 'HttpVersion' combinator in your API doesn't affect the client
259-- functions.
265-- functions.
260−instance HasClient api
266+instance HasClient m api
261− => HasClient (HttpVersion :> api) where
267+ => HasClient m (HttpVersion :> api) where
262
268
263 type Client m (HttpVersion :> api) =
269 type Client m (HttpVersion :> api) =
264 Client m api
270 Client m api
265
271
266− clientWithRoute Proxy =
272+ clientWithRoute pm Proxy =
267− clientWithRoute (Proxy :: Proxy api)
273+ clientWithRoute pm (Proxy :: Proxy api)
268
274
269-- | Ignore @'Summary'@ in client functions.
275-- | Ignore @'Summary'@ in client functions.
270−instance HasClient api => HasClient (Summary desc :> api) where
276+instance HasClient m api => HasClient m (Summary desc :> api) where
271 type Client m (Summary desc :> api) = Client m api
277 type Client m (Summary desc :> api) = Client m api
272
278
273− clientWithRoute _ = clientWithRoute (Proxy :: Proxy api)
279+ clientWithRoute pm _ = clientWithRoute pm (Proxy :: Proxy api)
274
280
275-- | Ignore @'Description'@ in client functions.
281-- | Ignore @'Description'@ in client functions.
276−instance HasClient api => HasClient (Description desc :> api) where
282+instance HasClient m api => HasClient m (Description desc :> api) where
277 type Client m (Description desc :> api) = Client m api
283 type Client m (Description desc :> api) = Client m api
278
284
279− clientWithRoute _ = clientWithRoute (Proxy :: Proxy api)
285+ clientWithRoute pm _ = clientWithRoute pm (Proxy :: Proxy api)
280
286
281-- | If you use a 'QueryParam' in one of your endpoints in your API,
287-- | If you use a 'QueryParam' in one of your endpoints in your API,
282-- the corresponding querying function will automatically take
288-- the corresponding querying function will automatically take
@@ -303,15 +309,15 @@ instance HasClient api => HasClient (Description desc :> api) where
303-- > -- then you can just use "getBooksBy" to query that endpoint.
309-- > -- then you can just use "getBooksBy" to query that endpoint.
304-- > -- 'getBooksBy Nothing' for all books
310-- > -- 'getBooksBy Nothing' for all books
305-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
311-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
306−instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
312+instance (KnownSymbol sym, ToHttpApiData a, HasClient m api)
307− => HasClient (QueryParam sym a :> api) where
313+ => HasClient m (QueryParam sym a :> api) where
308
314
309 type Client m (QueryParam sym a :> api) =
315 type Client m (QueryParam sym a :> api) =
310 Maybe a -> Client m api
316 Maybe a -> Client m api
311
317
312 -- if mparam = Nothing, we don't add it to the query string
318 -- if mparam = Nothing, we don't add it to the query string
313− clientWithRoute Proxy req mparam =
319+ clientWithRoute pm Proxy req mparam =
314− clientWithRoute (Proxy :: Proxy api)
320+ clientWithRoute pm (Proxy :: Proxy api)
315 (maybe req
321 (maybe req
316 (flip (appendToQueryString pname) req . Just)
322 (flip (appendToQueryString pname) req . Just)
317 mparamText
323 mparamText
@@ -348,14 +354,14 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
348-- > -- 'getBooksBy []' for all books
354-- > -- 'getBooksBy []' for all books
349-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
355-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
350-- > -- to get all books by Asimov and Heinlein
356-- > -- to get all books by Asimov and Heinlein
351−instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
357+instance (KnownSymbol sym, ToHttpApiData a, HasClient m api)
352− => HasClient (QueryParams sym a :> api) where
358+ => HasClient m (QueryParams sym a :> api) where
353
359
354 type Client m (QueryParams sym a :> api) =
360 type Client m (QueryParams sym a :> api) =
355 [a] -> Client m api
361 [a] -> Client m api
356
362
357− clientWithRoute Proxy req paramlist =
363+ clientWithRoute pm Proxy req paramlist =
358− clientWithRoute (Proxy :: Proxy api)
364+ clientWithRoute pm (Proxy :: Proxy api)
359 (foldl' (\ req' -> maybe req' (flip (appendToQueryString pname) req' . Just))
365 (foldl' (\ req' -> maybe req' (flip (appendToQueryString pname) req' . Just))
360 req
366 req
361 paramlist'
367 paramlist'
@@ -386,14 +392,14 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
386-- > -- then you can just use "getBooks" to query that endpoint.
392-- > -- then you can just use "getBooks" to query that endpoint.
387-- > -- 'getBooksBy False' for all books
393-- > -- 'getBooksBy False' for all books
388-- > -- 'getBooksBy True' to only get _already published_ books
394-- > -- 'getBooksBy True' to only get _already published_ books
389−instance (KnownSymbol sym, HasClient api)
395+instance (KnownSymbol sym, HasClient m api)
390− => HasClient (QueryFlag sym :> api) where
396+ => HasClient m (QueryFlag sym :> api) where
391
397
392 type Client m (QueryFlag sym :> api) =
398 type Client m (QueryFlag sym :> api) =
393 Bool -> Client m api
399 Bool -> Client m api
394
400
395− clientWithRoute Proxy req flag =
401+ clientWithRoute pm Proxy req flag =
396− clientWithRoute (Proxy :: Proxy api)
402+ clientWithRoute pm (Proxy :: Proxy api)
397 (if flag
403 (if flag
398 then appendToQueryString paramname Nothing req
404 then appendToQueryString paramname Nothing req
399 else req
405 else req
@@ -404,12 +410,13 @@ instance (KnownSymbol sym, HasClient api)
404
410
405-- | Pick a 'Method' and specify where the server you want to query is. You get
411-- | Pick a 'Method' and specify where the server you want to query is. You get
406-- back the full `Response`.
412-- back the full `Response`.
413+instance (RunClient m NoContent (Int, ByteString, MediaType, [HTTP.Header], Response ByteString))
407−instance HasClient Raw where
414+ => HasClient m Raw where
408 type Client m Raw
415 type Client m Raw
409 = H.Method -> m (Int, ByteString, MediaType, [HTTP.Header], Response ByteString)
416 = H.Method -> m (Int, ByteString, MediaType, [HTTP.Header], Response ByteString)
410
417
411− clientWithRoute :: Proxy Raw -> Req -> Client Raw
418+ clientWithRoute :: Proxy m -> Proxy Raw -> Req -> Client m Raw
412− clientWithRoute Proxy req httpMethod = do
419+ clientWithRoute pm Proxy req httpMethod = do
413 runRequest (Proxy :: Proxy NoContent) httpMethod req
420 runRequest (Proxy :: Proxy NoContent) httpMethod req
414
421
415-- | If you use a 'ReqBody' in one of your endpoints in your API,
422-- | If you use a 'ReqBody' in one of your endpoints in your API,
@@ -430,14 +437,14 @@ instance HasClient Raw where
430-- > addBook :: Book -> ClientM Book
437-- > addBook :: Book -> ClientM Book
431-- > addBook = client myApi
438-- > addBook = client myApi
432-- > -- then you can just use "addBook" to query that endpoint
439-- > -- then you can just use "addBook" to query that endpoint
433−instance (MimeRender ct a, HasClient api)
440+instance (MimeRender ct a, HasClient m api)
434− => HasClient (ReqBody (ct ': cts) a :> api) where
441+ => HasClient m (ReqBody (ct ': cts) a :> api) where
435
442
436 type Client m (ReqBody (ct ': cts) a :> api) =
443 type Client m (ReqBody (ct ': cts) a :> api) =
437 a -> Client m api
444 a -> Client m api
438
445
439− clientWithRoute Proxy req body =
446+ clientWithRoute pm Proxy req body =
440− clientWithRoute (Proxy :: Proxy api)
447+ clientWithRoute pm (Proxy :: Proxy api)
441 (let ctProxy = Proxy :: Proxy ct
448 (let ctProxy = Proxy :: Proxy ct
442 in setReqBodyLBS (mimeRender ctProxy body)
449 in setReqBodyLBS (mimeRender ctProxy body)
443 -- We use first contentType from the Accept list
450 -- We use first contentType from the Accept list
@@ -446,54 +453,54 @@ instance (MimeRender ct a, HasClient api)
446 )
453 )
447
454
448-- | Make the querying function append @path@ to the request path.
455-- | Make the querying function append @path@ to the request path.
449−instance (KnownSymbol path, HasClient api) => HasClient (path :> api) where
456+instance (KnownSymbol path, HasClient m api) => HasClient m (path :> api) where
450 type Client m (path :> api) = Client m api
457 type Client m (path :> api) = Client m api
451
458
452− clientWithRoute Proxy req =
459+ clientWithRoute pm Proxy req =
453− clientWithRoute (Proxy :: Proxy api)
460+ clientWithRoute pm (Proxy :: Proxy api)
454 (appendToPath p req)
461 (appendToPath p req)
455
462
456 where p = symbolVal (Proxy :: Proxy path)
463 where p = symbolVal (Proxy :: Proxy path)
457
464
458−instance HasClient api => HasClient (Vault :> api) where
465+instance HasClient m api => HasClient m (Vault :> api) where
459 type Client m (Vault :> api) = Client m api
466 type Client m (Vault :> api) = Client m api
460
467
461− clientWithRoute Proxy req =
468+ clientWithRoute pm Proxy req =
462− clientWithRoute (Proxy :: Proxy api) req
469+ clientWithRoute pm (Proxy :: Proxy api) req
463
470
464−instance HasClient api => HasClient (RemoteHost :> api) where
471+instance HasClient m api => HasClient m (RemoteHost :> api) where
465 type Client m (RemoteHost :> api) = Client m api
472 type Client m (RemoteHost :> api) = Client m api
466
473
467− clientWithRoute Proxy req =
474+ clientWithRoute pm Proxy req =
468− clientWithRoute (Proxy :: Proxy api) req
475+ clientWithRoute pm (Proxy :: Proxy api) req
469
476
470−instance HasClient api => HasClient (IsSecure :> api) where
477+instance HasClient m api => HasClient m (IsSecure :> api) where
471 type Client m (IsSecure :> api) = Client m api
478 type Client m (IsSecure :> api) = Client m api
472
479
473− clientWithRoute Proxy req =
480+ clientWithRoute pm Proxy req =
474− clientWithRoute (Proxy :: Proxy api) req
481+ clientWithRoute pm (Proxy :: Proxy api) req
475
482
476−instance HasClient subapi =>
483+instance HasClient m subapi =>
477− HasClient (WithNamedContext name context subapi) where
484+ HasClient m (WithNamedContext name context subapi) where
478
485
479 type Client m (WithNamedContext name context subapi) = Client m subapi
486 type Client m (WithNamedContext name context subapi) = Client m subapi
480− clientWithRoute Proxy = clientWithRoute (Proxy :: Proxy subapi)
487+ clientWithRoute pm Proxy = clientWithRoute pm (Proxy :: Proxy subapi)
481
488
482−instance ( HasClient api
489+instance ( HasClient m api
483− ) => HasClient (AuthProtect tag :> api) where
490+ ) => HasClient m (AuthProtect tag :> api) where
484 type Client m (AuthProtect tag :> api)
491 type Client m (AuthProtect tag :> api)
485 = AuthenticateReq (AuthProtect tag) -> Client m api
492 = AuthenticateReq (AuthProtect tag) -> Client m api
486
493
487− clientWithRoute Proxy req (AuthenticateReq (val,func)) =
494+ clientWithRoute pm Proxy req (AuthenticateReq (val,func)) =
488− clientWithRoute (Proxy :: Proxy api) (func val req)
495+ clientWithRoute pm (Proxy :: Proxy api) (func val req)
489
496
490-- * Basic Authentication
497-- * Basic Authentication
491
498
492−instance HasClient api => HasClient (BasicAuth realm usr :> api) where
499+instance HasClient m api => HasClient m (BasicAuth realm usr :> api) where
493 type Client m (BasicAuth realm usr :> api) = BasicAuthData -> Client m api
500 type Client m (BasicAuth realm usr :> api) = BasicAuthData -> Client m api
494
501
495− clientWithRoute Proxy req val =
502+ clientWithRoute pm Proxy req val =
496− clientWithRoute (Proxy :: Proxy api) (basicAuthReq val req)
503+ clientWithRoute pm (Proxy :: Proxy api) (basicAuthReq val req)
497
504
498
505
499{- Note [Non-Empty Content Types]
506{- Note [Non-Empty Content Types]
servant-client/src/Servant/Client/Class.hs+4−3
@@ -10,6 +10,7 @@ import Servant.API
10import Servant.Common.Req
10import Servant.Common.Req
11
11
12class (Monad m) => RunClient m ct result where
12class (Monad m) => RunClient m ct result where
13− runRequest :: MimeUnrender ct result
14− => Proxy ct
13+ runRequest :: Proxy ct
14+ -> Method
15+ -> Req
15− -> Method -> Req -> m result
16+ -> m result
servant-client/test/Servant/ClientSpec.hs+18−18
@@ -63,7 +63,7 @@ import Servant.Server
63import Servant.Server.Experimental.Auth
63import Servant.Server.Experimental.Auth
64
64
65-- This declaration simply checks that all instances are in place.
65-- This declaration simply checks that all instances are in place.
66−_ = client comprehensiveAPI
66+_ = client (Proxy :: Proxy ClientM) comprehensiveAPI
67
67
68spec :: Spec
68spec :: Spec
69spec = describe "Servant.Client" $ do
69spec = describe "Servant.Client" $ do
@@ -147,7 +147,7 @@ getGet
147 :<|> getMultiple
147 :<|> getMultiple
148 :<|> getRespHeaders
148 :<|> getRespHeaders
149 :<|> getDeleteContentType
149 :<|> getDeleteContentType
150− :<|> EmptyClient = client api
150+ :<|> EmptyClient = client (Proxy :: Proxy ClientM) api
151
151
152server :: Application
152server :: Application
153server = serve api (
153server = serve api (
@@ -241,7 +241,7 @@ data GenericClient = GenericClient
241 , mkNestedClient1 :: String -> NestedClient1
241 , mkNestedClient1 :: String -> NestedClient1
242 } deriving Generic
242 } deriving Generic
243instance SOP.Generic GenericClient
243instance SOP.Generic GenericClient
244−instance (Client GenericClientAPI ~ client) => ClientLike client GenericClient
244+instance (Client ClientM GenericClientAPI ~ client) => ClientLike client GenericClient
245
245
246type NestedAPI1
246type NestedAPI1
247 = QueryParam "int" Int :> NestedAPI2
247 = QueryParam "int" Int :> NestedAPI2
@@ -252,7 +252,7 @@ data NestedClient1 = NestedClient1
252 , idChar :: Maybe Char -> SCR.ClientM Char
252 , idChar :: Maybe Char -> SCR.ClientM Char
253 } deriving Generic
253 } deriving Generic
254instance SOP.Generic NestedClient1
254instance SOP.Generic NestedClient1
255−instance (Client NestedAPI1 ~ client) => ClientLike client NestedClient1
255+instance (Client ClientM NestedAPI1 ~ client) => ClientLike client NestedClient1
256
256
257type NestedAPI2
257type NestedAPI2
258 = "sum" :> Capture "first" Int :> Capture "second" Int :> Get '[JSON] Int
258 = "sum" :> Capture "first" Int :> Capture "second" Int :> Get '[JSON] Int
@@ -263,7 +263,7 @@ data NestedClient2 = NestedClient2
263 , doNothing :: SCR.ClientM ()
263 , doNothing :: SCR.ClientM ()
264 } deriving Generic
264 } deriving Generic
265instance SOP.Generic NestedClient2
265instance SOP.Generic NestedClient2
266−instance (Client NestedAPI2 ~ client) => ClientLike client NestedClient2
266+instance (Client ClientM NestedAPI2 ~ client) => ClientLike client NestedClient2
267
267
268genericClientServer :: Application
268genericClientServer :: Application
269genericClientServer = serve (Proxy :: Proxy GenericClientAPI) (
269genericClientServer = serve (Proxy :: Proxy GenericClientAPI) (
@@ -359,7 +359,7 @@ wrappedApiSpec = describe "error status codes" $ do
359 test (WrappedApi api, desc) =
359 test (WrappedApi api, desc) =
360 it desc $ bracket (startWaiApp $ serveW api) endWaiApp $ \(_, baseUrl) -> do
360 it desc $ bracket (startWaiApp $ serveW api) endWaiApp $ \(_, baseUrl) -> do
361 let getResponse :: SCR.ClientM ()
361 let getResponse :: SCR.ClientM ()
362− getResponse = client api
362+ getResponse = client (Proxy :: Proxy ClientM) api
363 Left FailureResponse{..} <- runClientM getResponse (ClientEnv manager baseUrl)
363 Left FailureResponse{..} <- runClientM getResponse (ClientEnv manager baseUrl)
364 responseStatus `shouldBe` (HTTP.Status 500 "error message")
364 responseStatus `shouldBe` (HTTP.Status 500 "error message")
365 in mapM_ test $
365 in mapM_ test $
@@ -374,35 +374,35 @@ failSpec = beforeAll (startWaiApp failServer) $ afterAll endWaiApp $ do
374
374
375 context "client returns errors appropriately" $ do
375 context "client returns errors appropriately" $ do
376 it "reports FailureResponse" $ \(_, baseUrl) -> do
376 it "reports FailureResponse" $ \(_, baseUrl) -> do
377− let (_ :<|> getDeleteEmpty :<|> _) = client api
377+ let (_ :<|> getDeleteEmpty :<|> _) = client (Proxy :: Proxy ClientM) api
378 Left res <- runClientM getDeleteEmpty (ClientEnv manager baseUrl)
378 Left res <- runClientM getDeleteEmpty (ClientEnv manager baseUrl)
379 case res of
379 case res of
380 FailureResponse _ (HTTP.Status 404 "Not Found") _ _ -> return ()
380 FailureResponse _ (HTTP.Status 404 "Not Found") _ _ -> return ()
381 _ -> fail $ "expected 404 response, but got " <> show res
381 _ -> fail $ "expected 404 response, but got " <> show res
382
382
383 it "reports DecodeFailure" $ \(_, baseUrl) -> do
383 it "reports DecodeFailure" $ \(_, baseUrl) -> do
384− let (_ :<|> _ :<|> getCapture :<|> _) = client api
384+ let (_ :<|> _ :<|> getCapture :<|> _) = client (Proxy :: Proxy ClientM) api
385 Left res <- runClientM (getCapture "foo") (ClientEnv manager baseUrl)
385 Left res <- runClientM (getCapture "foo") (ClientEnv manager baseUrl)
386 case res of
386 case res of
387 DecodeFailure _ ("application/json") _ -> return ()
387 DecodeFailure _ ("application/json") _ -> return ()
388 _ -> fail $ "expected DecodeFailure, but got " <> show res
388 _ -> fail $ "expected DecodeFailure, but got " <> show res
389
389
390 it "reports ConnectionError" $ \_ -> do
390 it "reports ConnectionError" $ \_ -> do
391− let (getGetWrongHost :<|> _) = client api
391+ let (getGetWrongHost :<|> _) = client (Proxy :: Proxy ClientM) api
392 Left res <- runClientM getGetWrongHost (ClientEnv manager (BaseUrl Http "127.0.0.1" 19872 ""))
392 Left res <- runClientM getGetWrongHost (ClientEnv manager (BaseUrl Http "127.0.0.1" 19872 ""))
393 case res of
393 case res of
394 ConnectionError _ -> return ()
394 ConnectionError _ -> return ()
395 _ -> fail $ "expected ConnectionError, but got " <> show res
395 _ -> fail $ "expected ConnectionError, but got " <> show res
396
396
397 it "reports UnsupportedContentType" $ \(_, baseUrl) -> do
397 it "reports UnsupportedContentType" $ \(_, baseUrl) -> do
398− let (getGet :<|> _ ) = client api
398+ let (getGet :<|> _ ) = client (Proxy :: Proxy ClientM) api
399 Left res <- runClientM getGet (ClientEnv manager baseUrl)
399 Left res <- runClientM getGet (ClientEnv manager baseUrl)
400 case res of
400 case res of
401 UnsupportedContentType ("application/octet-stream") _ -> return ()
401 UnsupportedContentType ("application/octet-stream") _ -> return ()
402 _ -> fail $ "expected UnsupportedContentType, but got " <> show res
402 _ -> fail $ "expected UnsupportedContentType, but got " <> show res
403
403
404 it "reports InvalidContentTypeHeader" $ \(_, baseUrl) -> do
404 it "reports InvalidContentTypeHeader" $ \(_, baseUrl) -> do
405− let (_ :<|> _ :<|> _ :<|> _ :<|> getBody :<|> _) = client api
405+ let (_ :<|> _ :<|> _ :<|> _ :<|> getBody :<|> _) = client (Proxy :: Proxy ClientM) api
406 Left res <- runClientM (getBody alice) (ClientEnv manager baseUrl)
406 Left res <- runClientM (getBody alice) (ClientEnv manager baseUrl)
407 case res of
407 case res of
408 InvalidContentTypeHeader "fooooo" _ -> return ()
408 InvalidContentTypeHeader "fooooo" _ -> return ()
@@ -410,7 +410,7 @@ failSpec = beforeAll (startWaiApp failServer) $ afterAll endWaiApp $ do
410
410
411data WrappedApi where
411data WrappedApi where
412 WrappedApi :: (HasServer (api :: *) '[], Server api ~ Handler a,
412 WrappedApi :: (HasServer (api :: *) '[], Server api ~ Handler a,
413− HasClient api, Client api ~ SCR.ClientM ()) =>
413+ HasClient ClientM api, Client ClientM api ~ SCR.ClientM ()) =>
414 Proxy api -> WrappedApi
414 Proxy api -> WrappedApi
415
415
416basicAuthSpec :: Spec
416basicAuthSpec :: Spec
@@ -418,14 +418,14 @@ basicAuthSpec = beforeAll (startWaiApp basicAuthServer) $ afterAll endWaiApp $ d
418 context "Authentication works when requests are properly authenticated" $ do
418 context "Authentication works when requests are properly authenticated" $ do
419
419
420 it "Authenticates a BasicAuth protected server appropriately" $ \(_,baseUrl) -> do
420 it "Authenticates a BasicAuth protected server appropriately" $ \(_,baseUrl) -> do
421− let getBasic = client basicAuthAPI
421+ let getBasic = client (Proxy :: Proxy ClientM) basicAuthAPI
422 let basicAuthData = BasicAuthData "servant" "server"
422 let basicAuthData = BasicAuthData "servant" "server"
423 (left show <$> runClientM (getBasic basicAuthData) (ClientEnv manager baseUrl)) `shouldReturn` Right alice
423 (left show <$> runClientM (getBasic basicAuthData) (ClientEnv manager baseUrl)) `shouldReturn` Right alice
424
424
425 context "Authentication is rejected when requests are not authenticated properly" $ do
425 context "Authentication is rejected when requests are not authenticated properly" $ do
426
426
427 it "Authenticates a BasicAuth protected server appropriately" $ \(_,baseUrl) -> do
427 it "Authenticates a BasicAuth protected server appropriately" $ \(_,baseUrl) -> do
428− let getBasic = client basicAuthAPI
428+ let getBasic = client (Proxy :: Proxy ClientM) basicAuthAPI
429 let basicAuthData = BasicAuthData "not" "password"
429 let basicAuthData = BasicAuthData "not" "password"
430 Left FailureResponse{..} <- runClientM (getBasic basicAuthData) (ClientEnv manager baseUrl)
430 Left FailureResponse{..} <- runClientM (getBasic basicAuthData) (ClientEnv manager baseUrl)
431 responseStatus `shouldBe` HTTP.Status 403 "Forbidden"
431 responseStatus `shouldBe` HTTP.Status 403 "Forbidden"
@@ -435,14 +435,14 @@ genAuthSpec = beforeAll (startWaiApp genAuthServer) $ afterAll endWaiApp $ do
435 context "Authentication works when requests are properly authenticated" $ do
435 context "Authentication works when requests are properly authenticated" $ do
436
436
437 it "Authenticates a AuthProtect protected server appropriately" $ \(_, baseUrl) -> do
437 it "Authenticates a AuthProtect protected server appropriately" $ \(_, baseUrl) -> do
438− let getProtected = client genAuthAPI
438+ let getProtected = client (Proxy :: Proxy ClientM) genAuthAPI
439 let authRequest = mkAuthenticateReq () (\_ req -> SCR.addHeader "AuthHeader" ("cool" :: String) req)
439 let authRequest = mkAuthenticateReq () (\_ req -> SCR.addHeader "AuthHeader" ("cool" :: String) req)
440 (left show <$> runClientM (getProtected authRequest) (ClientEnv manager baseUrl)) `shouldReturn` Right alice
440 (left show <$> runClientM (getProtected authRequest) (ClientEnv manager baseUrl)) `shouldReturn` Right alice
441
441
442 context "Authentication is rejected when requests are not authenticated properly" $ do
442 context "Authentication is rejected when requests are not authenticated properly" $ do
443
443
444 it "Authenticates a AuthProtect protected server appropriately" $ \(_, baseUrl) -> do
444 it "Authenticates a AuthProtect protected server appropriately" $ \(_, baseUrl) -> do
445− let getProtected = client genAuthAPI
445+ let getProtected = client (Proxy :: Proxy ClientM) genAuthAPI
446 let authRequest = mkAuthenticateReq () (\_ req -> SCR.addHeader "Wrong" ("header" :: String) req)
446 let authRequest = mkAuthenticateReq () (\_ req -> SCR.addHeader "Wrong" ("header" :: String) req)
447 Left FailureResponse{..} <- runClientM (getProtected authRequest) (ClientEnv manager baseUrl)
447 Left FailureResponse{..} <- runClientM (getProtected authRequest) (ClientEnv manager baseUrl)
448 responseStatus `shouldBe` (HTTP.Status 401 "Unauthorized")
448 responseStatus `shouldBe` (HTTP.Status 401 "Unauthorized")
@@ -451,11 +451,11 @@ genericClientSpec :: Spec
451genericClientSpec = beforeAll (startWaiApp genericClientServer) $ afterAll endWaiApp $ do
451genericClientSpec = beforeAll (startWaiApp genericClientServer) $ afterAll endWaiApp $ do
452 describe "Servant.Client.Generic" $ do
452 describe "Servant.Client.Generic" $ do
453
453
454− let GenericClient{..} = mkClient (client (Proxy :: Proxy GenericClientAPI))
454+ let GenericClient{..} = mkClient (client (Proxy :: Proxy ClientM) (Proxy :: Proxy GenericClientAPI))
455 NestedClient1{..} = mkNestedClient1 "example"
455 NestedClient1{..} = mkNestedClient1 "example"
456 NestedClient2{..} = mkNestedClient2 (Just 42)
456 NestedClient2{..} = mkNestedClient2 (Just 42)
457
457
458− it "works for top-level client function" $ \(_, baseUrl) -> do
458+ it "works for top-level client (Proxy :: Proxy ClientM) function" $ \(_, baseUrl) -> do
459 (left show <$> (runClientM (getSqr (Just 5)) (ClientEnv manager baseUrl))) `shouldReturn` Right 25
459 (left show <$> (runClientM (getSqr (Just 5)) (ClientEnv manager baseUrl))) `shouldReturn` Right 25
460
460
461 it "works for nested clients" $ \(_, baseUrl) -> do
461 it "works for nested clients" $ \(_, baseUrl) -> do