git.iko.soy/typeable/servant
on master help sign in
[wip] expose client runner as a typeclass
175c953Arnaud Bailly
servant-client/src/Servant/Client.hs+51−49
@@ -1,3 +1,4 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE CPP #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleContexts #-}
@@ -43,6 +44,7 @@ import Prelude.Compat
43import Servant.API
44import Servant.API
44import Servant.Client.Experimental.Auth
45import Servant.Client.Experimental.Auth
45import Servant.Client.HttpClient
46import Servant.Client.HttpClient
47+import Servant.Client.Class
46import Servant.Common.BaseUrl
48import Servant.Common.BaseUrl
47import Servant.Common.BasicAuth
49import Servant.Common.BasicAuth
48import Servant.Common.Req
50import Servant.Common.Req
@@ -60,15 +62,15 @@ import Servant.Common.Req
60-- > getAllBooks :: ClientM [Book]
62-- > getAllBooks :: ClientM [Book]
61-- > postNewBook :: Book -> ClientM Book
63-- > postNewBook :: Book -> ClientM Book
62-- > (getAllBooks :<|> postNewBook) = client myApi
64-- > (getAllBooks :<|> postNewBook) = client myApi
63−client :: HasClient api => Proxy api -> Client api
65+client :: HasClient api => Proxy api -> Client m api
64client p = clientWithRoute p defReq
66client p = clientWithRoute p defReq
65
67
66-- | This class lets us define how each API combinator
68-- | This class lets us define how each API combinator
67-- influences the creation of an HTTP request. It's mostly
69-- influences the creation of an HTTP request. It's mostly
68-- an internal class, you can just use 'client'.
70-- an internal class, you can just use 'client'.
69class HasClient api where
71class HasClient api where
70− type Client api :: *
72+ type Client (m :: * -> *) (api :: *) :: *
71− clientWithRoute :: Proxy api -> Req -> Client api
73+ clientWithRoute :: Proxy api -> Req -> Client m api
72
74
73
75
74-- | A client querying function for @a ':<|>' b@ will actually hand you
76-- | A client querying function for @a ':<|>' b@ will actually hand you
@@ -85,7 +87,7 @@ class HasClient api where
85-- > postNewBook :: Book -> ClientM Book
87-- > postNewBook :: Book -> ClientM Book
86-- > (getAllBooks :<|> postNewBook) = client myApi
88-- > (getAllBooks :<|> postNewBook) = client myApi
87instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
89instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
88− type Client (a :<|> b) = Client a :<|> Client b
90+ type Client m (a :<|> b) = Client m a :<|> Client m b
89 clientWithRoute Proxy req =
91 clientWithRoute Proxy req =
90 clientWithRoute (Proxy :: Proxy a) req :<|>
92 clientWithRoute (Proxy :: Proxy a) req :<|>
91 clientWithRoute (Proxy :: Proxy b) req
93 clientWithRoute (Proxy :: Proxy b) req
@@ -104,7 +106,7 @@ data EmptyClient = EmptyClient deriving (Eq, Show, Bounded, Enum)
104-- > getAllBooks :: ClientM [Book]
106-- > getAllBooks :: ClientM [Book]
105-- > (getAllBooks :<|> EmptyClient) = client myApi
107-- > (getAllBooks :<|> EmptyClient) = client myApi
106instance HasClient EmptyAPI where
108instance HasClient EmptyAPI where
107− type Client EmptyAPI = EmptyClient
109+ type Client m EmptyAPI = EmptyClient
108 clientWithRoute Proxy _ = EmptyClient
110 clientWithRoute Proxy _ = EmptyClient
109
111
110-- | If you use a 'Capture' in one of your endpoints in your API,
112-- | If you use a 'Capture' in one of your endpoints in your API,
@@ -129,8 +131,8 @@ instance HasClient EmptyAPI where
129instance (KnownSymbol capture, ToHttpApiData a, HasClient api)
131instance (KnownSymbol capture, ToHttpApiData a, HasClient api)
130 => HasClient (Capture capture a :> api) where
132 => HasClient (Capture capture a :> api) where
131
133
132− type Client (Capture capture a :> api) =
134+ type Client m (Capture capture a :> api) =
133− a -> Client api
135+ a -> Client m api
134
136
135 clientWithRoute Proxy req val =
137 clientWithRoute Proxy req val =
136 clientWithRoute (Proxy :: Proxy api)
138 clientWithRoute (Proxy :: Proxy api)
@@ -161,8 +163,8 @@ instance (KnownSymbol capture, ToHttpApiData a, HasClient api)
161instance (KnownSymbol capture, ToHttpApiData a, HasClient sublayout)
163instance (KnownSymbol capture, ToHttpApiData a, HasClient sublayout)
162 => HasClient (CaptureAll capture a :> sublayout) where
164 => HasClient (CaptureAll capture a :> sublayout) where
163
165
164− type Client (CaptureAll capture a :> sublayout) =
166+ type Client m (CaptureAll capture a :> sublayout) =
165− [a] -> Client sublayout
167+ [a] -> Client m sublayout
166
168
167 clientWithRoute Proxy req vals =
169 clientWithRoute Proxy req vals =
168 clientWithRoute (Proxy :: Proxy sublayout)
170 clientWithRoute (Proxy :: Proxy sublayout)
@@ -172,42 +174,42 @@ instance (KnownSymbol capture, ToHttpApiData a, HasClient sublayout)
172
174
173instance OVERLAPPABLE_
175instance OVERLAPPABLE_
174 -- Note [Non-Empty Content Types]
176 -- Note [Non-Empty Content Types]
175− (MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts)
177+ (RunClient m ct ([H.Header], a), MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts)
176 ) => HasClient (Verb method status cts' a) where
178 ) => HasClient (Verb method status cts' a) where
177− type Client (Verb method status cts' a) = ClientM a
179+ type Client m (Verb method status cts' a) = m a
178 clientWithRoute Proxy req = do
180 clientWithRoute Proxy req = do
179− snd <$> performRequestCT (Proxy :: Proxy ct) method req
181+ snd <$> runRequest (Proxy :: Proxy ct) method req
180 where method = reflectMethod (Proxy :: Proxy method)
182 where method = reflectMethod (Proxy :: Proxy method)
181
183
182instance OVERLAPPING_
184instance OVERLAPPING_
183− (ReflectMethod method) => HasClient (Verb method status cts NoContent) where
185+ (RunClient m NoContent [H.Header], ReflectMethod method) => HasClient (Verb method status cts NoContent) where
184− type Client (Verb method status cts NoContent)
186+ type Client m (Verb method status cts NoContent)
185− = ClientM NoContent
187+ = m NoContent
186 clientWithRoute Proxy req = do
188 clientWithRoute Proxy req = do
187− performRequestNoBody method req >> return NoContent
189+ runRequest (Proxy :: Proxy NoContent) method req >> return NoContent
188 where method = reflectMethod (Proxy :: Proxy method)
190 where method = reflectMethod (Proxy :: Proxy method)
189
191
190instance OVERLAPPING_
192instance OVERLAPPING_
191 -- Note [Non-Empty Content Types]
193 -- Note [Non-Empty Content Types]
192− ( MimeUnrender ct a, BuildHeadersTo ls, ReflectMethod method, cts' ~ (ct ': cts)
194+ ( RunClient m ct ([H.Header], a), MimeUnrender ct a, BuildHeadersTo ls, ReflectMethod method, cts' ~ (ct ': cts)
193 ) => HasClient (Verb method status cts' (Headers ls a)) where
195 ) => HasClient (Verb method status cts' (Headers ls a)) where
194− type Client (Verb method status cts' (Headers ls a))
196+ type Client m (Verb method status cts' (Headers ls a))
195− = ClientM (Headers ls a)
197+ = m (Headers ls a)
196 clientWithRoute Proxy req = do
198 clientWithRoute Proxy req = do
197 let method = reflectMethod (Proxy :: Proxy method)
199 let method = reflectMethod (Proxy :: Proxy method)
198− (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) method req
200+ (hdrs, resp) <- runRequest (Proxy :: Proxy ct) method req
199 return $ Headers { getResponse = resp
201 return $ Headers { getResponse = resp
200 , getHeadersHList = buildHeadersTo hdrs
202 , getHeadersHList = buildHeadersTo hdrs
201 }
203 }
202
204
203instance OVERLAPPING_
205instance OVERLAPPING_
204− ( BuildHeadersTo ls, ReflectMethod method
206+ ( RunClient m NoContent [H.Header], BuildHeadersTo ls, ReflectMethod method
205 ) => HasClient (Verb method status cts (Headers ls NoContent)) where
207 ) => HasClient (Verb method status cts (Headers ls NoContent)) where
206− type Client (Verb method status cts (Headers ls NoContent))
208+ type Client m (Verb method status cts (Headers ls NoContent))
207− = ClientM (Headers ls NoContent)
209+ = m (Headers ls NoContent)
208 clientWithRoute Proxy req = do
210 clientWithRoute Proxy req = do
209 let method = reflectMethod (Proxy :: Proxy method)
211 let method = reflectMethod (Proxy :: Proxy method)
210− hdrs <- performRequestNoBody method req
212+ hdrs <- runRequest method req
211 return $ Headers { getResponse = NoContent
213 return $ Headers { getResponse = NoContent
212 , getHeadersHList = buildHeadersTo hdrs
214 , getHeadersHList = buildHeadersTo hdrs
213 }
215 }
@@ -241,8 +243,8 @@ instance OVERLAPPING_
241instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
243instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
242 => HasClient (Header sym a :> api) where
244 => HasClient (Header sym a :> api) where
243
245
244− type Client (Header sym a :> api) =
246+ type Client m (Header sym a :> api) =
245− Maybe a -> Client api
247+ Maybe a -> Client m api
246
248
247 clientWithRoute Proxy req mval =
249 clientWithRoute Proxy req mval =
248 clientWithRoute (Proxy :: Proxy api)
250 clientWithRoute (Proxy :: Proxy api)
@@ -258,21 +260,21 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
258instance HasClient api
260instance HasClient api
259 => HasClient (HttpVersion :> api) where
261 => HasClient (HttpVersion :> api) where
260
262
261− type Client (HttpVersion :> api) =
263+ type Client m (HttpVersion :> api) =
262− Client api
264+ Client m api
263
265
264 clientWithRoute Proxy =
266 clientWithRoute Proxy =
265 clientWithRoute (Proxy :: Proxy api)
267 clientWithRoute (Proxy :: Proxy api)
266
268
267-- | Ignore @'Summary'@ in client functions.
269-- | Ignore @'Summary'@ in client functions.
268instance HasClient api => HasClient (Summary desc :> api) where
270instance HasClient api => HasClient (Summary desc :> api) where
269− type Client (Summary desc :> api) = Client api
271+ type Client m (Summary desc :> api) = Client m api
270
272
271 clientWithRoute _ = clientWithRoute (Proxy :: Proxy api)
273 clientWithRoute _ = clientWithRoute (Proxy :: Proxy api)
272
274
273-- | Ignore @'Description'@ in client functions.
275-- | Ignore @'Description'@ in client functions.
274instance HasClient api => HasClient (Description desc :> api) where
276instance HasClient api => HasClient (Description desc :> api) where
275− type Client (Description desc :> api) = Client api
277+ type Client m (Description desc :> api) = Client m api
276
278
277 clientWithRoute _ = clientWithRoute (Proxy :: Proxy api)
279 clientWithRoute _ = clientWithRoute (Proxy :: Proxy api)
278
280
@@ -304,8 +306,8 @@ instance HasClient api => HasClient (Description desc :> api) where
304instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
306instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
305 => HasClient (QueryParam sym a :> api) where
307 => HasClient (QueryParam sym a :> api) where
306
308
307− type Client (QueryParam sym a :> api) =
309+ type Client m (QueryParam sym a :> api) =
308− Maybe a -> Client api
310+ Maybe a -> Client m api
309
311
310 -- if mparam = Nothing, we don't add it to the query string
312 -- if mparam = Nothing, we don't add it to the query string
311 clientWithRoute Proxy req mparam =
313 clientWithRoute Proxy req mparam =
@@ -349,8 +351,8 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
349instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
351instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
350 => HasClient (QueryParams sym a :> api) where
352 => HasClient (QueryParams sym a :> api) where
351
353
352− type Client (QueryParams sym a :> api) =
354+ type Client m (QueryParams sym a :> api) =
353− [a] -> Client api
355+ [a] -> Client m api
354
356
355 clientWithRoute Proxy req paramlist =
357 clientWithRoute Proxy req paramlist =
356 clientWithRoute (Proxy :: Proxy api)
358 clientWithRoute (Proxy :: Proxy api)
@@ -387,8 +389,8 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
387instance (KnownSymbol sym, HasClient api)
389instance (KnownSymbol sym, HasClient api)
388 => HasClient (QueryFlag sym :> api) where
390 => HasClient (QueryFlag sym :> api) where
389
391
390− type Client (QueryFlag sym :> api) =
392+ type Client m (QueryFlag sym :> api) =
391− Bool -> Client api
393+ Bool -> Client m api
392
394
393 clientWithRoute Proxy req flag =
395 clientWithRoute Proxy req flag =
394 clientWithRoute (Proxy :: Proxy api)
396 clientWithRoute (Proxy :: Proxy api)
@@ -403,12 +405,12 @@ instance (KnownSymbol sym, HasClient api)
403-- | Pick a 'Method' and specify where the server you want to query is. You get
405-- | Pick a 'Method' and specify where the server you want to query is. You get
404-- back the full `Response`.
406-- back the full `Response`.
405instance HasClient Raw where
407instance HasClient Raw where
406− type Client Raw
408+ type Client m Raw
407− = H.Method -> ClientM (Int, ByteString, MediaType, [HTTP.Header], Response ByteString)
409+ = H.Method -> m (Int, ByteString, MediaType, [HTTP.Header], Response ByteString)
408
410
409 clientWithRoute :: Proxy Raw -> Req -> Client Raw
411 clientWithRoute :: Proxy Raw -> Req -> Client Raw
410 clientWithRoute Proxy req httpMethod = do
412 clientWithRoute Proxy req httpMethod = do
411− performRequest httpMethod req
413+ runRequest (Proxy :: Proxy NoContent) httpMethod req
412
414
413-- | If you use a 'ReqBody' in one of your endpoints in your API,
415-- | If you use a 'ReqBody' in one of your endpoints in your API,
414-- the corresponding querying function will automatically take
416-- the corresponding querying function will automatically take
@@ -431,8 +433,8 @@ instance HasClient Raw where
431instance (MimeRender ct a, HasClient api)
433instance (MimeRender ct a, HasClient api)
432 => HasClient (ReqBody (ct ': cts) a :> api) where
434 => HasClient (ReqBody (ct ': cts) a :> api) where
433
435
434− type Client (ReqBody (ct ': cts) a :> api) =
436+ type Client m (ReqBody (ct ': cts) a :> api) =
435− a -> Client api
437+ a -> Client m api
436
438
437 clientWithRoute Proxy req body =
439 clientWithRoute Proxy req body =
438 clientWithRoute (Proxy :: Proxy api)
440 clientWithRoute (Proxy :: Proxy api)
@@ -445,7 +447,7 @@ instance (MimeRender ct a, HasClient api)
445
447
446-- | Make the querying function append @path@ to the request path.
448-- | Make the querying function append @path@ to the request path.
447instance (KnownSymbol path, HasClient api) => HasClient (path :> api) where
449instance (KnownSymbol path, HasClient api) => HasClient (path :> api) where
448− type Client (path :> api) = Client api
450+ type Client m (path :> api) = Client m api
449
451
450 clientWithRoute Proxy req =
452 clientWithRoute Proxy req =
451 clientWithRoute (Proxy :: Proxy api)
453 clientWithRoute (Proxy :: Proxy api)
@@ -454,19 +456,19 @@ instance (KnownSymbol path, HasClient api) => HasClient (path :> api) where
454 where p = symbolVal (Proxy :: Proxy path)
456 where p = symbolVal (Proxy :: Proxy path)
455
457
456instance HasClient api => HasClient (Vault :> api) where
458instance HasClient api => HasClient (Vault :> api) where
457− type Client (Vault :> api) = Client api
459+ type Client m (Vault :> api) = Client m api
458
460
459 clientWithRoute Proxy req =
461 clientWithRoute Proxy req =
460 clientWithRoute (Proxy :: Proxy api) req
462 clientWithRoute (Proxy :: Proxy api) req
461
463
462instance HasClient api => HasClient (RemoteHost :> api) where
464instance HasClient api => HasClient (RemoteHost :> api) where
463− type Client (RemoteHost :> api) = Client api
465+ type Client m (RemoteHost :> api) = Client m api
464
466
465 clientWithRoute Proxy req =
467 clientWithRoute Proxy req =
466 clientWithRoute (Proxy :: Proxy api) req
468 clientWithRoute (Proxy :: Proxy api) req
467
469
468instance HasClient api => HasClient (IsSecure :> api) where
470instance HasClient api => HasClient (IsSecure :> api) where
469− type Client (IsSecure :> api) = Client api
471+ type Client m (IsSecure :> api) = Client m api
470
472
471 clientWithRoute Proxy req =
473 clientWithRoute Proxy req =
472 clientWithRoute (Proxy :: Proxy api) req
474 clientWithRoute (Proxy :: Proxy api) req
@@ -474,13 +476,13 @@ instance HasClient api => HasClient (IsSecure :> api) where
474instance HasClient subapi =>
476instance HasClient subapi =>
475 HasClient (WithNamedContext name context subapi) where
477 HasClient (WithNamedContext name context subapi) where
476
478
477− type Client (WithNamedContext name context subapi) = Client subapi
479+ type Client m (WithNamedContext name context subapi) = Client m subapi
478 clientWithRoute Proxy = clientWithRoute (Proxy :: Proxy subapi)
480 clientWithRoute Proxy = clientWithRoute (Proxy :: Proxy subapi)
479
481
480instance ( HasClient api
482instance ( HasClient api
481 ) => HasClient (AuthProtect tag :> api) where
483 ) => HasClient (AuthProtect tag :> api) where
482− type Client (AuthProtect tag :> api)
484+ type Client m (AuthProtect tag :> api)
483− = AuthenticateReq (AuthProtect tag) -> Client api
485+ = AuthenticateReq (AuthProtect tag) -> Client m api
484
486
485 clientWithRoute Proxy req (AuthenticateReq (val,func)) =
487 clientWithRoute Proxy req (AuthenticateReq (val,func)) =
486 clientWithRoute (Proxy :: Proxy api) (func val req)
488 clientWithRoute (Proxy :: Proxy api) (func val req)
@@ -488,7 +490,7 @@ instance ( HasClient api
488-- * Basic Authentication
490-- * Basic Authentication
489
491
490instance HasClient api => HasClient (BasicAuth realm usr :> api) where
492instance HasClient api => HasClient (BasicAuth realm usr :> api) where
491− type Client (BasicAuth realm usr :> api) = BasicAuthData -> Client api
493+ type Client m (BasicAuth realm usr :> api) = BasicAuthData -> Client m api
492
494
493 clientWithRoute Proxy req val =
495 clientWithRoute Proxy req val =
494 clientWithRoute (Proxy :: Proxy api) (basicAuthReq val req)
496 clientWithRoute (Proxy :: Proxy api) (basicAuthReq val req)
servant-client/src/Servant/Client/Class.hsnew file+15
@@ -0,0 +1,15 @@
1+{-# LANGUAGE MultiParamTypeClasses #-}
2+{-| Types for possible backends to run client-side `Req` queries -}
3+module Servant.Client.Class
4+ (RunClient(..))
5+where
6+
7+import Data.Proxy
8+import Network.HTTP.Types
9+import Servant.API
10+import Servant.Common.Req
11+
12+class (Monad m) => RunClient m ct result where
13+ runRequest :: MimeUnrender ct result
14+ => Proxy ct
15+ -> Method -> Req -> m result
servant-client/src/Servant/Client/HttpClient.hs+12
@@ -1,6 +1,7 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE DeriveGeneric #-}
4+{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeFamilies #-}
@@ -36,11 +37,22 @@ import Network.HTTP.Types
36import Network.HTTP.Client hiding (Proxy, path)
37import Network.HTTP.Client hiding (Proxy, path)
37import qualified Network.HTTP.Types.Header as HTTP
38import qualified Network.HTTP.Types.Header as HTTP
38import Servant.API.ContentTypes
39import Servant.API.ContentTypes
40+import Servant.Client.Class
39import Servant.Common.BaseUrl
41import Servant.Common.BaseUrl
40import Servant.Common.Req
42import Servant.Common.Req
41
43
42import qualified Network.HTTP.Client as Client
44import qualified Network.HTTP.Client as Client
43
45
46+instance RunClient ClientM NoContent ( Int, ByteString, MediaType
47+ , [HTTP.Header], Response ByteString) where
48+ runRequest _ meth req = performRequest meth req
49+
50+instance (MimeUnrender ct a) =>
51+ RunClient ClientM ct ([HTTP.Header], a) where
52+ runRequest p meth req = performRequestCT p meth req
53+
54+instance RunClient ClientM NoContent [HTTP.Header] where
55+ runRequest _ meth req = performRequestNoBody meth req
44
56
45data ClientEnv
57data ClientEnv
46 = ClientEnv
58 = ClientEnv