@@ -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 #-}
@@ -18,12 +19,14 @@
19module Servant.Reflex
20module Servant.Reflex
22+ , BuildHeaderKeysTo(..)
21 , HasClient(..)
24 , HasClient(..)
22 , module Servant.Common.Req
25 , module Servant.Common.Req
23 , module Servant.Common.BaseUrl
26 , module Servant.Common.BaseUrl
26−-------------------------------------------------------------------------------
29+------------------------------------------------------------------------------
27import Control.Applicative
30import Control.Applicative
28import Data.Monoid ((<>))
31import Data.Monoid ((<>))
29import qualified Data.Set as Set
32import qualified Data.Set as Set
@@ -43,6 +46,14 @@ import Servant.API ((:<|>)(..),(:>), BasicAuth,
43 QueryParams, Raw, ReflectMethod(..),
46 QueryParams, Raw, ReflectMethod(..),
44 RemoteHost, ReqBody,
47 RemoteHost, ReqBody,
45 ToHttpApiData(..), Vault, Verb)
48 ToHttpApiData(..), Vault, Verb)
50+import Reflex.Dom (Dynamic, Event, Reflex,
52+ XhrResponseHeaders(..),
53+ XhrResponse(..), ffor, fmapMaybe,
54+ leftmost, performRequestAsync,
56+------------------------------------------------------------------------------
46import Servant.Common.BaseUrl (BaseUrl(..), Scheme(..), baseUrlWidget,
57import Servant.Common.BaseUrl (BaseUrl(..), Scheme(..), baseUrlWidget,
47 showBaseUrl,
58 showBaseUrl,
48 SupportsServantReflex)
59 SupportsServantReflex)
@@ -50,17 +61,14 @@ import Servant.Common.Req (Req, ReqResult(..), QParam(..),
50 QueryPart(..), addHeader, authData,
61 QueryPart(..), addHeader, authData,
51 defReq, prependToPathParts,
62 defReq, prependToPathParts,
52 performRequestCT,
63 performRequestCT,
53 performRequestNoBody,
65 performRequestNoBody,
66+ performRequestsNoBody,
67+ performSomeRequestsAsync,
54 qParamToQueryPart, reqBody,
68 qParamToQueryPart, reqBody,
55 reqSuccess, reqFailure,
69 reqSuccess, reqFailure,
56 reqMethod, respHeaders, response,
70 reqMethod, respHeaders, response,
58−import Reflex.Dom (Dynamic, Event, Reflex,
60− XhrResponseHeaders(..),
61− XhrResponse(..), ffor, fmapMaybe,
62− leftmost, performRequestAsync,
65-- * Accessing APIs as a Client
73-- * Accessing APIs as a Client
@@ -81,6 +89,7 @@ client :: (HasClient t m layout)
81 => Proxy layout -> Proxy m -> Dynamic t BaseUrl -> Client t m layout
89 => Proxy layout -> Proxy m -> Dynamic t BaseUrl -> Client t m layout
82client p q baseurl = clientWithRoute p q defReq baseurl
90client p q baseurl = clientWithRoute p q defReq baseurl
84-- | This class lets us define how each API combinator
93-- | This class lets us define how each API combinator
85-- influences the creation of an HTTP request. It's mostly
94-- influences the creation of an HTTP request. It's mostly
86-- an internal class, you can just use 'client'.
95-- an internal class, you can just use 'client'.
@@ -91,10 +100,12 @@ class HasClient t m layout where
92instance (HasClient t m a, HasClient t m b) => HasClient t m (a :<|> b) where
101instance (HasClient t m a, HasClient t m b) => HasClient t m (a :<|> b) where
93 type Client t m (a :<|> b) = Client t m a :<|> Client t m b
102 type Client t m (a :<|> b) = Client t m a :<|> Client t m b
94 clientWithRoute Proxy q req baseurl =
104 clientWithRoute Proxy q req baseurl =
95 clientWithRoute (Proxy :: Proxy a) q req baseurl :<|>
105 clientWithRoute (Proxy :: Proxy a) q req baseurl :<|>
96 clientWithRoute (Proxy :: Proxy b) q req baseurl
106 clientWithRoute (Proxy :: Proxy b) q req baseurl
98-- Capture. Example:
109-- Capture. Example:
99-- > type MyApi = "books" :> Capture "isbn" Text :> Get '[JSON] Book
110-- > type MyApi = "books" :> Capture "isbn" Text :> Get '[JSON] Book
@@ -106,6 +117,7 @@ instance (HasClient t m a, HasClient t m b) => HasClient t m (a :<|> b) where
106-- -> Event t ()
117-- -> Event t ()
107-- -> m (Event t (Either XhrError (Text, Book)))
118-- -> m (Event t (Either XhrError (Text, Book)))
108-- > getBook = client myApi (constDyn host)
119-- > getBook = client myApi (constDyn host)
109instance (SupportsServantReflex t m, ToHttpApiData a, HasClient t m sublayout)
121instance (SupportsServantReflex t m, ToHttpApiData a, HasClient t m sublayout)
110 => HasClient t m (Capture capture a :> sublayout) where
122 => HasClient t m (Capture capture a :> sublayout) where
@@ -117,9 +129,9 @@ instance (SupportsServantReflex t m, ToHttpApiData a, HasClient t m sublayout)
118 (prependToPathParts p req)
130 (prependToPathParts p req)
121 where p = (fmap . fmap) (toUrlPiece) val
132 where p = (fmap . fmap) (toUrlPiece) val
123-- VERB (Returning content) --
135-- VERB (Returning content) --
124instance {-# OVERLAPPABLE #-}
136instance {-# OVERLAPPABLE #-}
125 -- Note [Non-Empty Content Types]
137 -- Note [Non-Empty Content Types]
@@ -134,6 +146,9 @@ instance {-# OVERLAPPABLE #-}
134 where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
146 where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
135 req' = req { reqMethod = method }
147 req' = req { reqMethod = method }
137-- -- VERB (No content) --
152-- -- VERB (No content) --
138instance {-# OVERLAPPING #-}
153instance {-# OVERLAPPING #-}
139 (ReflectMethod method, SupportsServantReflex t m) =>
154 (ReflectMethod method, SupportsServantReflex t m) =>
@@ -146,6 +161,7 @@ instance {-# OVERLAPPING #-}
146 performRequestNoBody method req baseurl
161 performRequestNoBody method req baseurl
147 where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
162 where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
149toHeaders :: BuildHeadersTo ls => ReqResult a -> ReqResult (Headers ls a)
165toHeaders :: BuildHeadersTo ls => ReqResult a -> ReqResult (Headers ls a)
150toHeaders r =
166toHeaders r =
151 let toBS = E.encodeUtf8
167 let toBS = E.encodeUtf8
@@ -186,6 +202,7 @@ instance {-# OVERLAPPABLE #-}
186 OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy :: Proxy ls)))
202 OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy :: Proxy ls)))
189-- HEADERS Verb (No content) --
206-- HEADERS Verb (No content) --
190instance {-# OVERLAPPABLE #-}
207instance {-# OVERLAPPABLE #-}
191 ( BuildHeadersTo ls, BuildHeaderKeysTo ls, ReflectMethod method,
208 ( BuildHeadersTo ls, BuildHeaderKeysTo ls, ReflectMethod method,
@@ -202,6 +219,7 @@ instance {-# OVERLAPPABLE #-}
206-- > newtype Referer = Referer { referrer :: Text }
224-- > newtype Referer = Referer { referrer :: Text }
207-- > deriving (Eq, Show, Generic, FromText, ToHttpApiData)
225-- > deriving (Eq, Show, Generic, FromText, ToHttpApiData)
@@ -229,6 +247,9 @@ instance (KnownSymbol sym, ToHttpApiData a,
230 where hname = T.pack $ symbolVal (Proxy :: Proxy sym)
248 where hname = T.pack $ symbolVal (Proxy :: Proxy sym)
232-- | Using a 'HttpVersion' combinator in your API doesn't affect the client
253-- | Using a 'HttpVersion' combinator in your API doesn't affect the client
233-- functions.
254-- functions.
234instance HasClient t m sublayout
255instance HasClient t m sublayout
@@ -241,6 +262,7 @@ instance HasClient t m sublayout
241 clientWithRoute (Proxy :: Proxy sublayout) q
262 clientWithRoute (Proxy :: Proxy sublayout) q
244-- | If you use a 'QueryParam' in one of your endpoints in your API,
266-- | If you use a 'QueryParam' in one of your endpoints in your API,
245-- the corresponding querying function will automatically take
267-- the corresponding querying function will automatically take
246-- an additional argument of the type specified by your 'QueryParam',
268-- an additional argument of the type specified by your 'QueryParam',
@@ -284,6 +306,8 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient t m sublayout, Reflex t)
284 p prm = QueryPartParam $ fmap qParamToQueryPart prm -- (fmap . fmap) (unpack . toQueryParam) prm
306 p prm = QueryPartParam $ fmap qParamToQueryPart prm -- (fmap . fmap) (unpack . toQueryParam) prm
285 paramPair = (T.pack pname, p mparam)
307 paramPair = (T.pack pname, p mparam)
287-- | If you use a 'QueryParams' in one of your endpoints in your API,
311-- | If you use a 'QueryParams' in one of your endpoints in your API,
288-- the corresponding querying function will automatically take
312-- the corresponding querying function will automatically take
289-- an additional argument, a list of values of the type specified
313-- an additional argument, a list of values of the type specified
@@ -327,6 +351,7 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient t m sublayout, Reflex t)
327 paramlist
351 paramlist
330-- | If you use a 'QueryFlag' in one of your endpoints in your API,
355-- | If you use a 'QueryFlag' in one of your endpoints in your API,
331-- the corresponding querying function will automatically take
356-- the corresponding querying function will automatically take
332-- an additional 'Bool' argument.
357-- an additional 'Bool' argument.
@@ -365,6 +390,7 @@ instance (KnownSymbol sym, HasClient t m sublayout, Reflex t)
365 pName = symbolVal (Proxy :: Proxy sym)
390 pName = symbolVal (Proxy :: Proxy sym)
368-- | Pick a 'Method' and specify where the server you want to query is. You get
394-- | Pick a 'Method' and specify where the server you want to query is. You get
369-- back the full `Response`.
395-- back the full `Response`.
370-- TODO redo
396-- TODO redo
@@ -389,6 +415,7 @@ instance SupportsServantReflex t m => HasClient t m Raw where
389 ,fmap RequestFailure badReq]
415 ,fmap RequestFailure badReq]
392hush :: Either e a -> Maybe a
419hush :: Either e a -> Maybe a
393hush = either (const Nothing) Just
420hush = either (const Nothing) Just
@@ -430,6 +457,8 @@ instance (MimeRender ct a, HasClient t m sublayout, Reflex t)
430 (\b -> (mimeRender ctProxy b, ctString))
457 (\b -> (mimeRender ctProxy b, ctString))
433-- | Make the querying function append @path@ to the request path.
462-- | Make the querying function append @path@ to the request path.
434instance (KnownSymbol path, HasClient t m sublayout, Reflex t) => HasClient t m (path :> sublayout) where
463instance (KnownSymbol path, HasClient t m sublayout, Reflex t) => HasClient t m (path :> sublayout) where
435 type Client t m (path :> sublayout) = Client t m sublayout
464 type Client t m (path :> sublayout) = Client t m sublayout
@@ -441,24 +470,29 @@ instance (KnownSymbol path, HasClient t m sublayout, Reflex t) => HasClient t m
442 where p = symbolVal (Proxy :: Proxy path)
471 where p = symbolVal (Proxy :: Proxy path)
444instance HasClient t m api => HasClient t m (Vault :> api) where
474instance HasClient t m api => HasClient t m (Vault :> api) where
445 type Client t m (Vault :> api) = Client t m api
475 type Client t m (Vault :> api) = Client t m api
447 clientWithRoute Proxy q req baseurl =
477 clientWithRoute Proxy q req baseurl =
448 clientWithRoute (Proxy :: Proxy api) q req baseurl
478 clientWithRoute (Proxy :: Proxy api) q req baseurl
450instance HasClient t m api => HasClient t m (RemoteHost :> api) where
481instance HasClient t m api => HasClient t m (RemoteHost :> api) where
451 type Client t m (RemoteHost :> api) = Client t m api
482 type Client t m (RemoteHost :> api) = Client t m api
453 clientWithRoute Proxy q req baseurl =
484 clientWithRoute Proxy q req baseurl =
454 clientWithRoute (Proxy :: Proxy api) q req baseurl
485 clientWithRoute (Proxy :: Proxy api) q req baseurl
456instance HasClient t m api => HasClient t m (IsSecure :> api) where
489instance HasClient t m api => HasClient t m (IsSecure :> api) where
457 type Client t m (IsSecure :> api) = Client t m api
490 type Client t m (IsSecure :> api) = Client t m api
459 clientWithRoute Proxy q req baseurl =
492 clientWithRoute Proxy q req baseurl =
460 clientWithRoute (Proxy :: Proxy api) q req baseurl
493 clientWithRoute (Proxy :: Proxy api) q req baseurl
462instance (HasClient t m api, Reflex t)
496instance (HasClient t m api, Reflex t)
463 => HasClient t m (BasicAuth realm usr :> api) where
497 => HasClient t m (BasicAuth realm usr :> api) where