@@ -1,24 +1,23 @@
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 GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE InstanceSigs #-}
7+{-# LANGUAGE InstanceSigs #-}
7−{-# LANGUAGE OverloadedStrings #-}
8−{-# LANGUAGE PolyKinds #-}
9−{-# LANGUAGE ScopedTypeVariables #-}
10−{-# LANGUAGE TupleSections #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE TypeOperators #-}
13−{-# LANGUAGE UndecidableInstances #-}
14−{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE MultiParamTypeClasses #-}
15−{-# LANGUAGE OverloadedStrings #-}
9+{-# LANGUAGE OverloadedStrings #-}
16−{-# LANGUAGE PolyKinds #-}
10+{-# LANGUAGE PolyKinds #-}
11+{-# LANGUAGE Rank2Types #-}
17−{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE ScopedTypeVariables #-}
18−{-# LANGUAGE TupleSections #-}
13+{-# LANGUAGE TupleSections #-}
19−{-# LANGUAGE TypeFamilies #-}
14+{-# LANGUAGE TypeFamilies #-}
20−{-# LANGUAGE TypeOperators #-}
15+{-# LANGUAGE TypeOperators #-}
21−{-# LANGUAGE UndecidableInstances #-}
16+{-# LANGUAGE UndecidableInstances #-}
18+#if MIN_VERSION_base(4,9,0)
19+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
23-- #include "overlapping-compat.h"
22-- #include "overlapping-compat.h"
24-- | This module provides 'client' which can automatically generate
23-- | This module provides 'client' which can automatically generate
@@ -26,47 +25,57 @@
27module Servant.Reflex
26module Servant.Reflex
29+ , clientWithOptsAndResultHandler
31+ , clientWithRouteAndResultHandler
29 , BuildHeaderKeysTo(..)
32 , BuildHeaderKeysTo(..)
30 , toHeaders
33 , toHeaders
31− , HasClient(..)
34+ , HasClient
32 , module Servant.Common.Req
36 , module Servant.Common.Req
33 , module Servant.Common.BaseUrl
37 , module Servant.Common.BaseUrl
36------------------------------------------------------------------------------
40------------------------------------------------------------------------------
38import Control.Applicative
41import Control.Applicative
39−import Data.Monoid ((<>))
40−import qualified Data.Set as Set
41−import qualified Data.Text.Encoding as E
42−import Data.CaseInsensitive (mk)
42+import qualified Data.CaseInsensitive as CI
43import Data.Functor.Identity
43import Data.Functor.Identity
44−import Data.Proxy (Proxy (..))
45import qualified Data.Map as Map
44import qualified Data.Map as Map
45+import Data.Monoid ((<>))
46+import Data.Proxy (Proxy (..))
47+import qualified Data.Set as Set
46import Data.Text (Text)
48import Data.Text (Text)
47import qualified Data.Text as T
49import qualified Data.Text as T
50+import qualified Data.Text.Encoding as E
51+import GHC.Exts (Constraint)
48import GHC.TypeLits (KnownSymbol, symbolVal)
52import GHC.TypeLits (KnownSymbol, symbolVal)
49−import Servant.API ((:<|>)(..),(:>), BasicAuth,
53+import Servant.API ((:<|>) (..), (:>), BasicAuth,
50− BasicAuthData, BuildHeadersTo(..),
54+ BasicAuthData, BuildHeadersTo (..),
51− Capture, contentType, Header,
55+ Capture, Header, Headers (..),
52− Headers(..), HttpVersion, IsSecure,
56+ HttpVersion, IsSecure,
53− MimeRender(..), MimeUnrender,
57+ MimeRender (..), MimeUnrender,
54 NoContent, QueryFlag, QueryParam,
58 NoContent, QueryFlag, QueryParam,
55− QueryParams, Raw, ReflectMethod(..),
59+ QueryParams, Raw, ReflectMethod (..),
56 RemoteHost, ReqBody,
60 RemoteHost, ReqBody,
57− ToHttpApiData(..), Vault, Verb)
61+ ToHttpApiData (..), Vault, Verb,
63+import Servant.API.Description (Summary)
64+import qualified Servant.Auth as Auth
59−import Reflex.Dom (Dynamic, Event, Reflex,
66+import Reflex.Dom.Core (Dynamic, Event, Reflex,
60− XhrRequest(..),
67+ XhrRequest (..), XhrResponse (..),
61− XhrResponseHeaders(..),
68+ XhrResponseHeaders (..),
62− XhrResponse(..), attachPromptlyDynWith, constDyn, ffor, fmapMaybe,
69+ attachPromptlyDynWith, constDyn, ffor,
63− leftmost, performRequestsAsync,
71+ performRequestsAsync)
65------------------------------------------------------------------------------
72------------------------------------------------------------------------------
66import Servant.Common.BaseUrl (BaseUrl(..), Scheme(..), baseUrlWidget,
73import Servant.Common.BaseUrl (BaseUrl(..), Scheme(..), baseUrlWidget,
67 showBaseUrl,
74 showBaseUrl,
68 SupportsServantReflex)
75 SupportsServantReflex)
76+import Servant.Common.Req (ClientOptions(..),
77+ defaultClientOptions,
69−import Servant.Common.Req (Req, ReqResult(..), QParam(..),
78+ Req, ReqResult(..), QParam(..),
70 QueryPart(..), addHeader, authData,
79 QueryPart(..), addHeader, authData,
71 defReq, evalResponse, prependToPathParts,
80 defReq, evalResponse, prependToPathParts,
72 -- performRequestCT,
81 -- performRequestCT,
@@ -76,9 +85,10 @@ import Servant.Common.Req (Req, ReqResult(..), QParam(..),
76 performSomeRequestsAsync,
85 performSomeRequestsAsync,
77 qParamToQueryPart, reqBody,
86 qParamToQueryPart, reqBody,
78 reqSuccess, reqFailure,
87 reqSuccess, reqFailure,
79− reqMethod, respHeaders, response,
88+ reqMethod, respHeaders,
81− qParams)
91+ qParams, withCredentials)
84-- * Accessing APIs as a Client
94-- * Accessing APIs as a Client
@@ -93,28 +103,75 @@ import Servant.Common.Req (Req, ReqResult(..), QParam(..),
94-- > getAllBooks :: Event t l -> m (Event t (l, ReqResult [Book]))
104-- > getAllBooks :: Event t l -> m (Event t (l, ReqResult [Book]))
95-- > postNewBook :: Dynamic t (Maybe Book) -> Event t l
105-- > postNewBook :: Dynamic t (Maybe Book) -> Event t l
96−-- -> m (Event t (l, ReqResult Book)))
106+-- > -> m (Event t (l, ReqResult Book)))
97-- > (getAllBooks :<|> postNewBook) = client myApi host
107-- > (getAllBooks :<|> postNewBook) = client myApi host
98-- > where host = constDyn $ BaseUrl Http "localhost" 8080
108-- > where host = constDyn $ BaseUrl Http "localhost" 8080
99−client :: (HasClient t m layout tag)
110+ :: (HasClient t m layout tag)
114+ -> Dynamic t BaseUrl
115+ -> Client t m layout tag
116+client p q t baseurl = clientWithRoute p q t defReq baseurl defaultClientOptions
119+ :: (HasClient t m layout tag)
123+ -> Dynamic t BaseUrl
100− => Proxy layout -> Proxy m -> Proxy tag -> Dynamic t BaseUrl -> Client t m layout tag
125+ -> Client t m layout tag
101−client p q t baseurl = clientWithRoute p q t defReq baseurl
126+clientWithOpts p q t baseurl = clientWithRoute p q t defReq baseurl
128+-- | Like 'clientWithOpts' but allows passing a function which will process the
129+-- result event in some way. This can be used to handle errors in a uniform way
130+-- across call sites.
131+clientWithOptsAndResultHandler
132+ :: (HasClient t m layout tag)
136+ -> Dynamic t BaseUrl
138+ -> (forall a. Event t (ReqResult tag a) -> m (Event t (ReqResult tag a)))
139+ -> Client t m layout tag
140+clientWithOptsAndResultHandler p q t = clientWithRouteAndResultHandler p q t defReq
104-- | This class lets us define how each API combinator
143-- | This class lets us define how each API combinator
105-- influences the creation of an HTTP request. It's mostly
144-- influences the creation of an HTTP request. It's mostly
106-- an internal class, you can just use 'client'.
145-- an internal class, you can just use 'client'.
107−class HasClient t m layout (tag :: *) where
146+class Monad m => HasClient t m layout (tag :: *) where
108 type Client t m layout tag :: *
147 type Client t m layout tag :: *
109− clientWithRoute :: Proxy layout -> Proxy m -> Proxy tag -> Req t -> Dynamic t BaseUrl -> Client t m layout tag
148+ clientWithRoute
153+ -> Dynamic t BaseUrl
155+ -> Client t m layout tag
156+ clientWithRoute l m t r b o = clientWithRouteAndResultHandler l m t r b o return
158+ clientWithRouteAndResultHandler
163+ -> Dynamic t BaseUrl
165+ -> (forall a. Event t (ReqResult tag a) -> m (Event t (ReqResult tag a)))
166+ -> Client t m layout tag
112instance (HasClient t m a tag, HasClient t m b tag) => HasClient t m (a :<|> b) tag where
169instance (HasClient t m a tag, HasClient t m b tag) => HasClient t m (a :<|> b) tag where
113 type Client t m (a :<|> b) tag = Client t m a tag :<|> Client t m b tag
170 type Client t m (a :<|> b) tag = Client t m a tag :<|> Client t m b tag
115− clientWithRoute Proxy q pTag req baseurl =
172+ clientWithRouteAndResultHandler Proxy q pTag req baseurl opts wrap =
116− clientWithRoute (Proxy :: Proxy a) q pTag req baseurl :<|>
173+ clientWithRouteAndResultHandler (Proxy :: Proxy a) q pTag req baseurl opts wrap :<|>
117− clientWithRoute (Proxy :: Proxy b) q pTag req baseurl
174+ clientWithRouteAndResultHandler (Proxy :: Proxy b) q pTag req baseurl opts wrap
120-- Capture. Example:
177-- Capture. Example:
@@ -129,32 +186,29 @@ instance (HasClient t m a tag, HasClient t m b tag) => HasClient t m (a :<|> b)
129-- -> m (Event t (l, ReqResult Book))
186-- -> m (Event t (l, ReqResult Book))
130-- > getBook = client myApi (constDyn host)
187-- > getBook = client myApi (constDyn host)
132−instance (SupportsServantReflex t m, FromJSON a, ToHttpApiData a, HasClient t m sublayout tag)
189+instance (SupportsServantReflex t m, ToHttpApiData a, HasClient t m sublayout tag)
133 => HasClient t m (Capture capture a :> sublayout) tag where
190 => HasClient t m (Capture capture a :> sublayout) tag where
135 type Client t m (Capture capture a :> sublayout) tag =
192 type Client t m (Capture capture a :> sublayout) tag =
136 Dynamic t (Either Text a) -> Client t m sublayout tag
193 Dynamic t (Either Text a) -> Client t m sublayout tag
138− clientWithRoute Proxy q t req baseurl val =
195+ clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap val =
139− clientWithRoute (Proxy :: Proxy sublayout)
196+ clientWithRouteAndResultHandler
141− (prependToPathParts p req)
197+ (Proxy :: Proxy sublayout) q t (prependToPathParts p req) baseurl opts wrap
143 where p = (fmap . fmap) (toUrlPiece) val
198 where p = (fmap . fmap) (toUrlPiece) val
146-- VERB (Returning content) --
201-- VERB (Returning content) --
147instance {-# OVERLAPPABLE #-}
202instance {-# OVERLAPPABLE #-}
148 -- Note [Non-Empty Content Types]
203 -- Note [Non-Empty Content Types]
149− (FromJSON a, MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts), SupportsServantReflex t m
204+ (MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts), SupportsServantReflex t m
150 ) => HasClient t m (Verb method status cts' a) tag where
205 ) => HasClient t m (Verb method status cts' a) tag where
151 type Client t m (Verb method status cts' a) tag =
206 type Client t m (Verb method status cts' a) tag =
152 Event t tag -> m (Event t (ReqResult tag a))
207 Event t tag -> m (Event t (ReqResult tag a))
153 -- TODO how to access input types here?
208 -- TODO how to access input types here?
154 -- ExceptT ServantError IO a
209 -- ExceptT ServantError IO a
155− clientWithRoute Proxy _ _ req baseurl trigs =
210+ clientWithRouteAndResultHandler Proxy _ _ req baseurl opts wrap trigs =
156− -- performRequestCT (Proxy :: Proxy ct) method req' baseurl trigs
157− fmap runIdentity <$> performRequestsCT (Proxy :: Proxy ct) method (constDyn $ Identity $ req') baseurl trigs
211+ wrap =<< fmap runIdentity <$> performRequestsCT (Proxy :: Proxy ct) method (constDyn $ Identity $ req') baseurl opts trigs
158 where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
212 where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
159 req' = req { reqMethod = method }
213 req' = req { reqMethod = method }
@@ -167,29 +221,33 @@ instance {-# OVERLAPPING #-}
167 Event t tag -> m (Event t (ReqResult tag NoContent))
221 Event t tag -> m (Event t (ReqResult tag NoContent))
168 -- TODO: how to access input types here?
222 -- TODO: how to access input types here?
169 -- ExceptT ServantError IO NoContent
223 -- ExceptT ServantError IO NoContent
170− clientWithRoute Proxy _ _ req baseurl =
224+ clientWithRouteAndResultHandler Proxy _ _ req baseurl opts wrap trigs =
171− (fmap . fmap) runIdentity . performRequestsNoBody method (constDyn $ Identity req) baseurl
225+ wrap =<< fmap runIdentity <$> performRequestsNoBody method (constDyn $ Identity req) baseurl opts trigs
172 where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
226 where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
175toHeaders :: BuildHeadersTo ls => ReqResult tag a -> ReqResult tag (Headers ls a)
229toHeaders :: BuildHeadersTo ls => ReqResult tag a -> ReqResult tag (Headers ls a)
176toHeaders r =
230toHeaders r =
177− let toBS = E.encodeUtf8
178− hdrs = maybe []
231+ let hdrs = maybe []
179− (\xhr -> fmap (\(h,v) -> (mk (toBS h), toBS v))
232+ (\xhr -> fmap (\(h,v) -> (CI.map E.encodeUtf8 h, E.encodeUtf8 v))
180 (Map.toList $ _xhrResponse_headers xhr))
233 (Map.toList $ _xhrResponse_headers xhr))
181 (response r)
234 (response r)
182 in ffor r $ \a -> Headers {getResponse = a ,getHeadersHList = buildHeadersTo hdrs}
235 in ffor r $ \a -> Headers {getResponse = a ,getHeadersHList = buildHeadersTo hdrs}
184class BuildHeaderKeysTo hs where
238class BuildHeaderKeysTo hs where
185− buildHeaderKeysTo :: Proxy hs -> [T.Text]
239+ buildHeaderKeysTo :: Proxy hs -> [CI.CI T.Text]
187instance {-# OVERLAPPABLE #-} BuildHeaderKeysTo '[]
241instance {-# OVERLAPPABLE #-} BuildHeaderKeysTo '[]
188 where buildHeaderKeysTo _ = []
242 where buildHeaderKeysTo _ = []
190instance {-# OVERLAPPABLE #-} (BuildHeaderKeysTo xs, KnownSymbol h)
244instance {-# OVERLAPPABLE #-} (BuildHeaderKeysTo xs, KnownSymbol h)
191 => BuildHeaderKeysTo ((Header h v) ': xs) where
245 => BuildHeaderKeysTo ((Header h v) ': xs) where
246+ buildHeaderKeysTo _ =
248+ thisKey = CI.mk $ T.pack (symbolVal (Proxy :: Proxy h))
192− buildHeaderKeysTo _ = T.pack (symbolVal (Proxy :: Proxy h)) : buildHeaderKeysTo (Proxy :: Proxy xs)
249+ in thisKey : buildHeaderKeysTo (Proxy :: Proxy xs)
194-- HEADERS Verb (Content) --
252-- HEADERS Verb (Content) --
195-- Headers combinator not treated in fully general case,
253-- Headers combinator not treated in fully general case,
@@ -199,16 +257,15 @@ instance {-# OVERLAPPABLE #-} (BuildHeaderKeysTo xs, KnownSymbol h)
199instance {-# OVERLAPPABLE #-}
257instance {-# OVERLAPPABLE #-}
200 -- Note [Non-Empty Content Types]
258 -- Note [Non-Empty Content Types]
201 ( MimeUnrender ct a, BuildHeadersTo ls, BuildHeaderKeysTo ls,
259 ( MimeUnrender ct a, BuildHeadersTo ls, BuildHeaderKeysTo ls,
203 ReflectMethod method, cts' ~ (ct ': cts),
260 ReflectMethod method, cts' ~ (ct ': cts),
204 SupportsServantReflex t m
261 SupportsServantReflex t m
205 ) => HasClient t m (Verb method status cts' (Headers ls a)) tag where
262 ) => HasClient t m (Verb method status cts' (Headers ls a)) tag where
206 type Client t m (Verb method status cts' (Headers ls a)) tag =
263 type Client t m (Verb method status cts' (Headers ls a)) tag =
207 Event t tag -> m (Event t (ReqResult tag (Headers ls a)))
264 Event t tag -> m (Event t (ReqResult tag (Headers ls a)))
208− clientWithRoute Proxy _ _ req baseurl trigs = do
265+ clientWithRouteAndResultHandler Proxy _ _ req baseurl opts wrap trigs = do
209 let method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
266 let method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
210− resp <- fmap runIdentity <$> performRequestsCT (Proxy :: Proxy ct) method (constDyn $ Identity req') baseurl trigs
267+ resp <- fmap runIdentity <$> performRequestsCT (Proxy :: Proxy ct) method (constDyn $ Identity req') baseurl opts trigs
211− return $ toHeaders <$> resp
268+ wrap $ toHeaders <$> resp
212 where req' = req { respHeaders =
269 where req' = req { respHeaders =
213 OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy :: Proxy ls)))
270 OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy :: Proxy ls)))
@@ -221,10 +278,10 @@ instance {-# OVERLAPPABLE #-}
221 ) => HasClient t m (Verb method status cts (Headers ls NoContent)) tag where
278 ) => HasClient t m (Verb method status cts (Headers ls NoContent)) tag where
222 type Client t m (Verb method status cts (Headers ls NoContent)) tag
279 type Client t m (Verb method status cts (Headers ls NoContent)) tag
223 = Event t tag -> m (Event t (ReqResult tag (Headers ls NoContent)))
280 = Event t tag -> m (Event t (ReqResult tag (Headers ls NoContent)))
224− clientWithRoute Proxy _ _ req baseurl trigs = do
281+ clientWithRouteAndResultHandler Proxy _ _ req baseurl opts wrap trigs = do
225 let method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
282 let method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
226− resp <- fmap runIdentity <$> performRequestsNoBody method (constDyn $ Identity req') baseurl trigs
283+ resp <- fmap runIdentity <$> performRequestsNoBody method (constDyn $ Identity req') baseurl opts trigs
227− return $ toHeaders <$> resp
284+ wrap $ toHeaders <$> resp
228 where req' = req {respHeaders =
285 where req' = req {respHeaders =
229 OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy :: Proxy ls)))
286 OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy :: Proxy ls)))
@@ -251,11 +308,11 @@ instance (KnownSymbol sym, ToHttpApiData a,
251 type Client t m (Header sym a :> sublayout) tag =
308 type Client t m (Header sym a :> sublayout) tag =
252 Dynamic t (Either Text a) -> Client t m sublayout tag
309 Dynamic t (Either Text a) -> Client t m sublayout tag
254− clientWithRoute Proxy q t req baseurl eVal =
311+ clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap eVal =
312+ clientWithRouteAndResultHandler
255− clientWithRoute (Proxy :: Proxy sublayout)
313+ (Proxy :: Proxy sublayout) q t
257− (Servant.Common.Req.addHeader hname eVal req)
314+ (Servant.Common.Req.addHeader hname eVal req)
258− baseurl
315+ baseurl opts wrap
259 where hname = T.pack $ symbolVal (Proxy :: Proxy sym)
316 where hname = T.pack $ symbolVal (Proxy :: Proxy sym)
@@ -269,8 +326,21 @@ instance HasClient t m sublayout tag
269 type Client t m (HttpVersion :> sublayout) tag =
326 type Client t m (HttpVersion :> sublayout) tag =
270 Client t m sublayout tag
327 Client t m sublayout tag
272− clientWithRoute Proxy q t =
329+ clientWithRouteAndResultHandler Proxy =
330+ clientWithRouteAndResultHandler (Proxy :: Proxy sublayout)
332+-- | Using a 'Summary' combinator in your API doesn't affect the client
334+instance (HasClient t m sublayout tag, KnownSymbol sym)
335+ => HasClient t m (Summary sym :> sublayout) tag where
337+ type Client t m (Summary sym :> sublayout) tag =
338+ Client t m sublayout tag
340+ clientWithRouteAndResultHandler Proxy =
273− clientWithRoute (Proxy :: Proxy sublayout) q t
341+ clientWithRouteAndResultHandler (Proxy :: Proxy sublayout)
276-- | If you use a 'QueryParam' in one of your endpoints in your API,
346-- | If you use a 'QueryParam' in one of your endpoints in your API,
@@ -306,9 +376,9 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient t m sublayout tag, Reflex
306 Dynamic t (QParam a) -> Client t m sublayout tag
376 Dynamic t (QParam a) -> Client t m sublayout tag
308 -- if mparam = Nothing, we don't add it to the query string
378 -- if mparam = Nothing, we don't add it to the query string
309− clientWithRoute Proxy q t req baseurl mparam =
379+ clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap mparam =
310− clientWithRoute (Proxy :: Proxy sublayout) q t
380+ clientWithRouteAndResultHandler (Proxy :: Proxy sublayout) q t
311− (req {qParams = paramPair : qParams req}) baseurl
381+ (req {qParams = paramPair : qParams req}) baseurl opts wrap
313 where pname = symbolVal (Proxy :: Proxy sym)
383 where pname = symbolVal (Proxy :: Proxy sym)
314 --p prm = QueryPartParam $ (fmap . fmap) (toQueryParam) prm
384 --p prm = QueryPartParam $ (fmap . fmap) (toQueryParam) prm
@@ -352,8 +422,8 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient t m sublayout tag, Reflex
352 type Client t m (QueryParams sym a :> sublayout) tag =
422 type Client t m (QueryParams sym a :> sublayout) tag =
353 Dynamic t [a] -> Client t m sublayout tag
423 Dynamic t [a] -> Client t m sublayout tag
355− clientWithRoute Proxy q t req baseurl paramlist =
425+ clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap paramlist =
356− clientWithRoute (Proxy :: Proxy sublayout) q t req' baseurl
426+ clientWithRouteAndResultHandler (Proxy :: Proxy sublayout) q t req' baseurl opts wrap
358 where req' = req { qParams = (T.pack pname, params') : qParams req }
428 where req' = req { qParams = (T.pack pname, params') : qParams req }
359 pname = symbolVal (Proxy :: Proxy sym)
429 pname = symbolVal (Proxy :: Proxy sym)
@@ -392,8 +462,8 @@ instance (KnownSymbol sym, HasClient t m sublayout tag, Reflex t)
392 type Client t m (QueryFlag sym :> sublayout) tag =
462 type Client t m (QueryFlag sym :> sublayout) tag =
393 Dynamic t Bool -> Client t m sublayout tag
463 Dynamic t Bool -> Client t m sublayout tag
395− clientWithRoute Proxy q t req baseurl flag =
465+ clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap flag =
396− clientWithRoute (Proxy :: Proxy sublayout) q t req' baseurl
466+ clientWithRouteAndResultHandler (Proxy :: Proxy sublayout) q t req' baseurl opts wrap
398 where req' = req { qParams = thisPair : qParams req }
468 where req' = req { qParams = thisPair : qParams req }
399 thisPair = (T.pack pName, QueryPartFlag flag) :: (Text, QueryPart t)
469 thisPair = (T.pack pName, QueryPartFlag flag) :: (Text, QueryPart t)
@@ -407,7 +477,7 @@ instance SupportsServantReflex t m => HasClient t m Raw tag where
407 -> Event t tag
477 -> Event t tag
408 -> m (Event t (ReqResult tag ()))
478 -> m (Event t (ReqResult tag ()))
410− clientWithRoute _ _ _ _ baseurl xhrs triggers = do
480+ clientWithRouteAndResultHandler _ _ _ _ baseurl _ wrap xhrs triggers = do
412 let xhrs' = liftA2 (\x path -> case x of
482 let xhrs' = liftA2 (\x path -> case x of
413 Left e -> Left e
483 Left e -> Left e
@@ -418,7 +488,7 @@ instance SupportsServantReflex t m => HasClient t m Raw tag where
418 okReq = fmapMaybe (\(t,x) -> either (const Nothing) (Just . (t,)) x) xhrs'' :: Event t (tag, XhrRequest ())
488 okReq = fmapMaybe (\(t,x) -> either (const Nothing) (Just . (t,)) x) xhrs'' :: Event t (tag, XhrRequest ())
420 resps <- performRequestsAsync okReq
490 resps <- performRequestsAsync okReq
421− return $ leftmost [ uncurry RequestFailure <$> badReq
491+ wrap $ leftmost [ uncurry RequestFailure <$> badReq
422 , evalResponse (const $ Right ()) <$> resps
492 , evalResponse (const $ Right ()) <$> resps
@@ -449,8 +519,8 @@ instance (MimeRender ct a, HasClient t m sublayout tag, Reflex t)
449 type Client t m (ReqBody (ct ': cts) a :> sublayout) tag =
519 type Client t m (ReqBody (ct ': cts) a :> sublayout) tag =
450 Dynamic t (Either Text a) -> Client t m sublayout tag
520 Dynamic t (Either Text a) -> Client t m sublayout tag
452− clientWithRoute Proxy q t req baseurl body =
522+ clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap body =
453− clientWithRoute (Proxy :: Proxy sublayout) q t req' baseurl
523+ clientWithRouteAndResultHandler (Proxy :: Proxy sublayout) q t req' baseurl opts wrap
454 where req' = req { reqBody = bodyBytesCT }
524 where req' = req { reqBody = bodyBytesCT }
455 ctProxy = Proxy :: Proxy ct
525 ctProxy = Proxy :: Proxy ct
456 ctString = T.pack $ show $ contentType ctProxy
526 ctString = T.pack $ show $ contentType ctProxy
@@ -464,9 +534,9 @@ instance (MimeRender ct a, HasClient t m sublayout tag, Reflex t)
464instance (KnownSymbol path, HasClient t m sublayout tag, Reflex t) => HasClient t m (path :> sublayout) tag where
534instance (KnownSymbol path, HasClient t m sublayout tag, Reflex t) => HasClient t m (path :> sublayout) tag where
465 type Client t m (path :> sublayout) tag = Client t m sublayout tag
535 type Client t m (path :> sublayout) tag = Client t m sublayout tag
467− clientWithRoute Proxy q t req baseurl =
537+ clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap =
468− clientWithRoute (Proxy :: Proxy sublayout) q t
538+ clientWithRouteAndResultHandler (Proxy :: Proxy sublayout) q t
469− (prependToPathParts (pure (Right $ T.pack p)) req) baseurl
539+ (prependToPathParts (pure (Right $ T.pack p)) req) baseurl opts wrap
471 where p = symbolVal (Proxy :: Proxy path)
541 where p = symbolVal (Proxy :: Proxy path)
@@ -474,23 +544,23 @@ instance (KnownSymbol path, HasClient t m sublayout tag, Reflex t) => HasClient
474instance HasClient t m api tag => HasClient t m (Vault :> api) tag where
544instance HasClient t m api tag => HasClient t m (Vault :> api) tag where
475 type Client t m (Vault :> api) tag = Client t m api tag
545 type Client t m (Vault :> api) tag = Client t m api tag
477− clientWithRoute Proxy q t req baseurl =
547+ clientWithRouteAndResultHandler Proxy =
478− clientWithRoute (Proxy :: Proxy api) q t req baseurl
548+ clientWithRouteAndResultHandler (Proxy :: Proxy api)
481instance HasClient t m api tag => HasClient t m (RemoteHost :> api) tag where
551instance HasClient t m api tag => HasClient t m (RemoteHost :> api) tag where
482 type Client t m (RemoteHost :> api) tag = Client t m api tag
552 type Client t m (RemoteHost :> api) tag = Client t m api tag
484− clientWithRoute Proxy q t req baseurl =
554+ clientWithRouteAndResultHandler Proxy =
485− clientWithRoute (Proxy :: Proxy api) q t req baseurl
555+ clientWithRouteAndResultHandler (Proxy :: Proxy api)
489instance HasClient t m api tag => HasClient t m (IsSecure :> api) tag where
559instance HasClient t m api tag => HasClient t m (IsSecure :> api) tag where
490 type Client t m (IsSecure :> api) tag = Client t m api tag
560 type Client t m (IsSecure :> api) tag = Client t m api tag
492− clientWithRoute Proxy q t req baseurl =
562+ clientWithRouteAndResultHandler Proxy =
493− clientWithRoute (Proxy :: Proxy api) q t req baseurl
563+ clientWithRouteAndResultHandler (Proxy :: Proxy api)
496instance (HasClient t m api tag, Reflex t)
566instance (HasClient t m api tag, Reflex t)
@@ -499,8 +569,8 @@ instance (HasClient t m api tag, Reflex t)
499 type Client t m (BasicAuth realm usr :> api) tag = Dynamic t (Maybe BasicAuthData)
569 type Client t m (BasicAuth realm usr :> api) tag = Dynamic t (Maybe BasicAuthData)
500 -> Client t m api tag
570 -> Client t m api tag
502− clientWithRoute Proxy q t req baseurl authdata =
572+ clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap authdata =
503− clientWithRoute (Proxy :: Proxy api) q t req' baseurl
573+ clientWithRouteAndResultHandler (Proxy :: Proxy api) q t req' baseurl opts wrap
505 req' = req { authData = Just authdata }
575 req' = req { authData = Just authdata }
@@ -526,3 +596,30 @@ non-empty lists, but is otherwise more specific, no instance will be overall
526more specific. This in turn generally means adding yet another instance (one
596more specific. This in turn generally means adding yet another instance (one
527for empty and one for non-empty lists).
597for empty and one for non-empty lists).
601+-- SUPPORT FOR servant-auth --
603+-- For JavaScript clients we should be sending/storing JSON web tokens in a
604+-- way that is inaccessible to JavaScript.
606+-- For @servant-auth@ this is done with HTTP-only cookies. In a Reflex-DOM
607+-- app this means the @servant-auth@ client should only verify that the API
608+-- supports Cookie-based authentication but do nothing with the token
611+-- @HasCookieAuth auths@ is nominally a redundant constraint, but ensures
612+-- we're not trying to rely on cookies when the API does not use them.
613+instance (HasCookieAuth auths, HasClient t m api tag) => HasClient t m (Auth.Auth auths a :> api) tag where
615+ type Client t m (Auth.Auth auths a :> api) tag = Client t m api tag
616+ clientWithRouteAndResultHandler Proxy = clientWithRouteAndResultHandler (Proxy :: Proxy api)
619+type family HasCookieAuth xs :: Constraint where
620+ HasCookieAuth (Auth.Cookie ': xs) = ()
621+ HasCookieAuth (x ': xs) = HasCookieAuth xs
622+ HasCookieAuth '[] = CookieAuthNotEnabled
624+class CookieAuthNotEnabled