@@ -1,23 +1,16 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
1+{-# LANGUAGE CPP #-}
2−{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE InstanceSigs #-}
6−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6+{-# LANGUAGE OverloadedStrings #-}
7−{-# LANGUAGE InstanceSigs #-}
7+{-# LANGUAGE PolyKinds #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE OverloadedStrings #-}
9+{-# LANGUAGE TupleSections #-}
10−{-# LANGUAGE PolyKinds #-}
10+{-# LANGUAGE TypeFamilies #-}
11−{-# LANGUAGE Rank2Types #-}
11+{-# LANGUAGE TypeOperators #-}
12−{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE UndecidableInstances #-}
13−{-# LANGUAGE TupleSections #-}
13+{-# LANGUAGE MultiParamTypeClasses #-}
14−{-# LANGUAGE TypeFamilies #-}
15−{-# LANGUAGE TypeOperators #-}
16−{-# LANGUAGE UndecidableInstances #-}
18−#if MIN_VERSION_base(4,9,0)
19−{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
22-- #include "overlapping-compat.h"
15-- #include "overlapping-compat.h"
23-- | This module provides 'client' which can automatically generate
16-- | This module provides 'client' which can automatically generate
@@ -25,71 +18,30 @@
26module Servant.Reflex
19module Servant.Reflex
28− , clientWithOpts
21+ , HasClient(..)
29− , clientWithOptsAndResultHandler
31− , clientWithRouteAndResultHandler
32− , BuildHeaderKeysTo(..)
36− , module Servant.Common.Req
37 , module Servant.Common.BaseUrl
22 , module Servant.Common.BaseUrl
40−------------------------------------------------------------------------------
25+import Control.Applicative ((<$>))
41−import Control.Applicative
26+import Control.Monad.Trans.Except
42−import qualified Data.CaseInsensitive as CI
27+import qualified Data.ByteString.Char8 as BS
43−import Data.Functor.Identity
28+import Data.ByteString.Lazy (ByteString)
44−import qualified Data.Map as Map
29+import qualified Data.CaseInsensitive as CI
45−import Data.Monoid ((<>))
30+import Data.List
46−import Data.Proxy (Proxy (..))
31+import Data.Proxy
47−import qualified Data.Set as Set
32+import Data.String.Conversions
48−import Data.Text (Text)
33+import Data.Maybe (maybeToList)
49−import qualified Data.Text as T
34+import Data.Text (unpack)
50−import qualified Data.Text.Encoding as E
35+import Data.Traversable (sequenceA)
51−import GHC.Exts (Constraint)
36+import GHC.TypeLits
52−import GHC.TypeLits (KnownSymbol, symbolVal)
37+import qualified Network.HTTP.Media as M
53−import Servant.API ((:<|>) (..), (:>), BasicAuth,
38+import Servant.API
54− BasicAuthData, BuildHeadersTo (..),
39+import Servant.Common.BaseUrl
55− Capture, Header, Headers (..),
40+import Servant.Common.Req
56− HttpVersion, IsSecure,
41+import Servant.API.ContentTypes
57− MimeRender (..), MimeUnrender,
42+import Reflex
58− NoContent, QueryFlag, QueryParam,
43+import Reflex.Dom
59− QueryParams, Raw, ReflectMethod (..),
44+import Reflex.Dom.Xhr
61− ToHttpApiData (..), Vault, Verb,
63−import Servant.API.Description (Summary)
64−import qualified Servant.Auth as Auth
66−import Reflex.Dom.Core (Dynamic, Event, Reflex,
67− XhrRequest (..), XhrResponse (..),
68− XhrResponseHeaders (..),
69− attachPromptlyDynWith, constDyn, ffor,
71− performRequestsAsync)
72−------------------------------------------------------------------------------
73−import Servant.Common.BaseUrl (BaseUrl(..), Scheme(..), baseUrlWidget,
75− SupportsServantReflex)
76−import Servant.Common.Req (ClientOptions(..),
77− defaultClientOptions,
78− Req, ReqResult(..), QParam(..),
79− QueryPart(..), addHeader, authData,
80− defReq, evalResponse, prependToPathParts,
83− -- performRequestNoBody,
84− performRequestsNoBody,
85− performSomeRequestsAsync,
86− qParamToQueryPart, reqBody,
87− reqSuccess, reqFailure,
88− reqMethod, respHeaders,
91− qParams, withCredentials)
94-- * Accessing APIs as a Client
46-- * Accessing APIs as a Client
@@ -101,191 +53,116 @@ import Servant.Common.Req (ClientOptions(..),
101-- > myApi :: Proxy MyApi
53-- > myApi :: Proxy MyApi
102-- > myApi = Proxy
54-- > myApi = Proxy
104−-- > getAllBooks :: Event t l -> m (Event t (l, ReqResult [Book]))
56+-- > getAllBooks :: Event t () -> m (Event t (Either XhrError ((),[Book])))
105−-- > postNewBook :: Dynamic t (Maybe Book) -> Event t l
57+-- > postNewBook :: Behavior t (Maybe Book) -> Event t ()
106−-- > -> m (Event t (l, ReqResult Book)))
58+-- -> m (Event t (Either XhrError (Book,Book)))
107-- > (getAllBooks :<|> postNewBook) = client myApi host
59-- > (getAllBooks :<|> postNewBook) = client myApi host
108-- > where host = constDyn $ BaseUrl Http "localhost" 8080
60-- > where host = constDyn $ BaseUrl Http "localhost" 8080
109−client
61+client :: (HasClient t m layout, MonadWidget t m)
110− :: (HasClient t m layout tag)
62+ => Proxy layout -> Proxy m -> Dynamic t BaseUrl -> Client t m layout
111− => Proxy layout
63+client p q baseurl = clientWithRoute p q defReq baseurl
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
125− -> Client t m layout tag
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
143-- | This class lets us define how each API combinator
65-- | This class lets us define how each API combinator
144-- influences the creation of an HTTP request. It's mostly
66-- influences the creation of an HTTP request. It's mostly
145-- an internal class, you can just use 'client'.
67-- an internal class, you can just use 'client'.
146−class Monad m => HasClient t m layout (tag :: *) where
68+class HasClient t m layout where
147− type Client t m layout tag :: *
69+ type Client t m layout :: *
148− clientWithRoute
70+ clientWithRoute :: Proxy layout -> Proxy m -> Req t -> Dynamic t BaseUrl -> Client t m layout
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
169−instance (HasClient t m a tag, HasClient t m b tag) => HasClient t m (a :<|> b) tag where
170− type Client t m (a :<|> b) tag = Client t m a tag :<|> Client t m b tag
172− clientWithRouteAndResultHandler Proxy q pTag req baseurl opts wrap =
173− clientWithRouteAndResultHandler (Proxy :: Proxy a) q pTag req baseurl opts wrap :<|>
174− clientWithRouteAndResultHandler (Proxy :: Proxy b) q pTag req baseurl opts wrap
73+instance (HasClient t m a, HasClient t m b) => HasClient t m (a :<|> b) where
74+ type Client t m (a :<|> b) = Client t m a :<|> Client t m b
75+ clientWithRoute Proxy q req baseurl =
76+ clientWithRoute (Proxy :: Proxy a) q req baseurl :<|>
77+ clientWithRoute (Proxy :: Proxy b) q req baseurl
177-- Capture. Example:
79-- Capture. Example:
178-- > type MyApi = "books" :> Capture "isbn" Text :> Get '[JSON] Book
80-- > type MyApi = "books" :> Capture "isbn" Text :> Get '[JSON] Book
180-- > myApi :: Proxy MyApi = Proxy
82-- > myApi :: Proxy MyApi = Proxy
182−-- > getBook :: SupportsServantReflex t m
84+-- > getBook :: MonadWidget t m
183-- => Dynamic t BaseUrl
85-- => Dynamic t BaseUrl
184−-- -> Dynamic t (Maybe Text)
86+-- -> Behavior t (Maybe Text)
185−-- -> Event t l
87+-- -> Event t ()
186−-- -> m (Event t (l, ReqResult Book))
88+-- -> m (Event t (Either XhrError (Text, Book)))
187-- > getBook = client myApi (constDyn host)
89-- > getBook = client myApi (constDyn host)
90+instance (MonadWidget t m, KnownSymbol capture, ToHttpApiData a, HasClient t m sublayout)
91+ => HasClient t m (Capture capture a :> sublayout) where
189−instance (SupportsServantReflex t m, ToHttpApiData a, HasClient t m sublayout tag)
93+ type Client t m (Capture capture a :> sublayout) =
190− => HasClient t m (Capture capture a :> sublayout) tag where
94+ Behavior t (Maybe a) -> Client t m sublayout
192− type Client t m (Capture capture a :> sublayout) tag =
193− Dynamic t (Either Text a) -> Client t m sublayout tag
195− clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap val =
96+ clientWithRoute Proxy q req baseurl val =
196− clientWithRouteAndResultHandler
97+ clientWithRoute (Proxy :: Proxy sublayout)
197− (Proxy :: Proxy sublayout) q t (prependToPathParts p req) baseurl opts wrap
98+ q
198− where p = (fmap . fmap) (toUrlPiece) val
99+ (prependToPathParts p req)
102+ where p = (fmap . fmap) (unpack . toUrlPiece) val
201-- VERB (Returning content) --
104-- VERB (Returning content) --
202instance {-# OVERLAPPABLE #-}
105instance {-# OVERLAPPABLE #-}
203 -- Note [Non-Empty Content Types]
106 -- Note [Non-Empty Content Types]
204− (MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts), SupportsServantReflex t m
107+ (MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts), MonadWidget t m
205− ) => HasClient t m (Verb method status cts' a) tag where
108+ ) => HasClient t m (Verb method status cts' a) where
206− type Client t m (Verb method status cts' a) tag =
109+ type Client t m (Verb method status cts' a) =
207− Event t tag -> m (Event t (ReqResult tag a))
110+ Event t () -> m (Event t (Maybe a, XhrResponse))
208 -- TODO how to access input types here?
111 -- TODO how to access input types here?
209 -- ExceptT ServantError IO a
112 -- ExceptT ServantError IO a
210− clientWithRouteAndResultHandler Proxy _ _ req baseurl opts wrap trigs =
113+ clientWithRoute Proxy q req baseurl =
211− wrap =<< fmap runIdentity <$> performRequestsCT (Proxy :: Proxy ct) method (constDyn $ Identity $ req') baseurl opts trigs
114+ performRequestCT (Proxy :: Proxy ct) method req' baseurl
212− where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
115+ where method = BS.unpack $ reflectMethod (Proxy :: Proxy method)
213 req' = req { reqMethod = method }
116 req' = req { reqMethod = method }
216-- -- VERB (No content) --
118-- -- VERB (No content) --
217instance {-# OVERLAPPING #-}
119instance {-# OVERLAPPING #-}
218− (ReflectMethod method, SupportsServantReflex t m) =>
120+ (ReflectMethod method, MonadWidget t m) =>
219− HasClient t m (Verb method status cts NoContent) tag where
121+ HasClient t m (Verb method status cts NoContent) where
220− type Client t m (Verb method status cts NoContent) tag =
122+ type Client t m (Verb method status cts NoContent) =
221− Event t tag -> m (Event t (ReqResult tag NoContent))
123+ Event t () -> m (Event t (Maybe NoContent, XhrResponse))
222 -- TODO: how to access input types here?
124 -- TODO: how to access input types here?
223 -- ExceptT ServantError IO NoContent
125 -- ExceptT ServantError IO NoContent
224− clientWithRouteAndResultHandler Proxy _ _ req baseurl opts wrap trigs =
126+ clientWithRoute Proxy q req baseurl =
225− wrap =<< fmap runIdentity <$> performRequestsNoBody method (constDyn $ Identity req) baseurl opts trigs
127+ performRequestNoBody method req baseurl
226− where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
128+ where method = BS.unpack $ reflectMethod (Proxy :: Proxy method)
228−
130+-- -- HEADERS Verb (Content) --
229−toHeaders :: BuildHeadersTo ls => ReqResult tag a -> ReqResult tag (Headers ls a)
131+-- -- Headers combinator not treated in fully general case,
230−toHeaders r =
132+-- -- in order to deny instances for (Headers ls (Capture "id" Int)),
231− let hdrs = maybe []
133+-- -- a combinator that wouldn't make sense
232− (\xhr -> fmap (\(h,v) -> (CI.map E.encodeUtf8 h, E.encodeUtf8 v))
134+-- -- TODO Overlapping??
233− (Map.toList $ _xhrResponse_headers xhr))
135+-- instance {-# OVERLAPPABLE #-}
234− (response r)
136+-- -- Note [Non-Empty Content Types]
235− in ffor r $ \a -> Headers {getResponse = a ,getHeadersHList = buildHeadersTo hdrs}
137+-- ( MimeUnrender ct a, BuildHeadersTo ls,
138+-- ReflectMethod method, cts' ~ (ct ': cts),
140+-- ) => HasClient t m (Verb method status cts' (Headers ls a)) where
141+-- type Client t m (Verb method status cts' (Headers ls a))
142+-- = Event t () -> m (Event t (Maybe a, XhrResponse))
143+-- -- ExceptT ServantError IO (Headers ls a)
144+-- clientWithRoute Proxy req baseurl = do
145+-- let method = reflectMethod (Proxy :: Proxy method)
146+-- (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) method req baseurl
147+-- return $ Headers { getResponse = resp
148+-- , getHeadersHList = buildHeadersTo hdrs
238−class BuildHeaderKeysTo hs where
239− buildHeaderKeysTo :: Proxy hs -> [CI.CI T.Text]
241−instance {-# OVERLAPPABLE #-} BuildHeaderKeysTo '[]
242− where buildHeaderKeysTo _ = []
244−instance {-# OVERLAPPABLE #-} (BuildHeaderKeysTo xs, KnownSymbol h)
245− => BuildHeaderKeysTo ((Header h v) ': xs) where
246− buildHeaderKeysTo _ =
248− thisKey = CI.mk $ T.pack (symbolVal (Proxy :: Proxy h))
249− in thisKey : buildHeaderKeysTo (Proxy :: Proxy xs)
252−-- HEADERS Verb (Content) --
253−-- Headers combinator not treated in fully general case,
254−-- in order to deny instances for (Headers ls (Capture "id" Int)),
255−-- a combinator that wouldn't make sense
256-- TODO Overlapping??
151-- TODO Overlapping??
257−instance {-# OVERLAPPABLE #-}
152+-- -- HEADERS Verb (No content) --
258− -- Note [Non-Empty Content Types]
153+-- instance {-# OVERLAPPABLE #-}
259− ( MimeUnrender ct a, BuildHeadersTo ls, BuildHeaderKeysTo ls,
154+-- ( BuildHeadersTo ls, ReflectMethod method,
260− ReflectMethod method, cts' ~ (ct ': cts),
155+-- MonadWidget t m
261− SupportsServantReflex t m
156+-- ) => HasClient t m (Verb method status cts (Headers ls NoContent)) where
262− ) => HasClient t m (Verb method status cts' (Headers ls a)) tag where
157+-- type Client t m (Verb method status cts (Headers ls NoContent))
263− type Client t m (Verb method status cts' (Headers ls a)) tag =
158+-- = Event t () -> m (Event t XhrResponse)
264− Event t tag -> m (Event t (ReqResult tag (Headers ls a)))
159+-- -- ExceptT ServantError IO (Headers ls NoContent)
265− clientWithRouteAndResultHandler Proxy _ _ req baseurl opts wrap trigs = do
160+-- clientWithRoute Proxy req baseurl = do
266− let method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
161+-- let method = reflectMethod (Proxy :: Proxy method)
267− resp <- fmap runIdentity <$> performRequestsCT (Proxy :: Proxy ct) method (constDyn $ Identity req') baseurl opts trigs
162+-- hdrs <- performRequestNoBody method req baseurl
268− wrap $ toHeaders <$> resp
163+-- return $ Headers { getResponse = NoContent
269− where req' = req { respHeaders =
164+-- , getHeadersHList = buildHeadersTo hdrs
270− OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy :: Proxy ls)))
165+-- }
274−-- HEADERS Verb (No content) --
275−instance {-# OVERLAPPABLE #-}
276− ( BuildHeadersTo ls, BuildHeaderKeysTo ls, ReflectMethod method,
277− SupportsServantReflex t m
278− ) => HasClient t m (Verb method status cts (Headers ls NoContent)) tag where
279− type Client t m (Verb method status cts (Headers ls NoContent)) tag
280− = Event t tag -> m (Event t (ReqResult tag (Headers ls NoContent)))
281− clientWithRouteAndResultHandler Proxy _ _ req baseurl opts wrap trigs = do
282− let method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
283− resp <- fmap runIdentity <$> performRequestsNoBody method (constDyn $ Identity req') baseurl opts trigs
284− wrap $ toHeaders <$> resp
285− where req' = req {respHeaders =
286− OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy :: Proxy ls)))
@@ -302,46 +179,34 @@ instance {-# OVERLAPPABLE #-}
302-- > -- then you can just use "viewRefer" to query that endpoint
179-- > -- then you can just use "viewRefer" to query that endpoint
303-- > -- specifying Nothing or e.g Just "http://haskell.org/" as arguments
180-- > -- specifying Nothing or e.g Just "http://haskell.org/" as arguments
304instance (KnownSymbol sym, ToHttpApiData a,
181instance (KnownSymbol sym, ToHttpApiData a,
305− HasClient t m sublayout tag, SupportsServantReflex t m)
182+ HasClient t m sublayout, MonadWidget t m)
306− => HasClient t m (Header sym a :> sublayout) tag where
183+ => HasClient t m (Header sym a :> sublayout) where
308− type Client t m (Header sym a :> sublayout) tag =
309− Dynamic t (Either Text a) -> Client t m sublayout tag
311− clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap eVal =
312− clientWithRouteAndResultHandler
313− (Proxy :: Proxy sublayout) q t
314− (Servant.Common.Req.addHeader hname eVal req)
316− where hname = T.pack $ symbolVal (Proxy :: Proxy sym)
185+ type Client t m (Header sym a :> sublayout) =
186+ Behavior t (Maybe a) -> Client t m sublayout
188+ clientWithRoute Proxy q req baseurl mval =
189+ clientWithRoute (Proxy :: Proxy sublayout)
192+ -- (maybe req -- TODO Need to pass the header in
193+ -- (\value -> Servant.Common.Req.addHeader hname value req)
198+ where hname = symbolVal (Proxy :: Proxy sym)
321-- | Using a 'HttpVersion' combinator in your API doesn't affect the client
200-- | Using a 'HttpVersion' combinator in your API doesn't affect the client
322-- functions.
201-- functions.
323−instance HasClient t m sublayout tag
202+instance HasClient t m sublayout
324− => HasClient t m (HttpVersion :> sublayout) tag where
203+ => HasClient t m (HttpVersion :> sublayout) where
326− type Client t m (HttpVersion :> sublayout) tag =
327− Client t m sublayout tag
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 =
341− clientWithRouteAndResultHandler (Proxy :: Proxy sublayout)
205+ type Client t m (HttpVersion :> sublayout) =
206+ Client t m sublayout
208+ clientWithRoute Proxy q =
209+ clientWithRoute (Proxy :: Proxy sublayout) q
346-- | If you use a 'QueryParam' in one of your endpoints in your API,
211-- | If you use a 'QueryParam' in one of your endpoints in your API,
347-- the corresponding querying function will automatically take
212-- the corresponding querying function will automatically take
@@ -369,24 +234,21 @@ instance (HasClient t m sublayout tag, KnownSymbol sym)
369-- > -- then you can just use "getBooksBy" to query that endpoint.
234-- > -- then you can just use "getBooksBy" to query that endpoint.
370-- > -- 'getBooksBy Nothing' for all books
235-- > -- 'getBooksBy Nothing' for all books
371-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
236-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
372−instance (KnownSymbol sym, ToHttpApiData a, HasClient t m sublayout tag, Reflex t)
237+instance (KnownSymbol sym, ToHttpApiData a, HasClient t m sublayout, Reflex t)
373− => HasClient t m (QueryParam sym a :> sublayout) tag where
238+ => HasClient t m (QueryParam sym a :> sublayout) where
375− type Client t m (QueryParam sym a :> sublayout) tag =
240+ type Client t m (QueryParam sym a :> sublayout) =
376− Dynamic t (QParam a) -> Client t m sublayout tag
241+ -- TODO (Maybe a), or (Maybe (Maybe a))? (should the user be able to send a Nothing)
242+ Behavior t (Maybe a) -> Client t m sublayout
378 -- if mparam = Nothing, we don't add it to the query string
244 -- if mparam = Nothing, we don't add it to the query string
379− clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap mparam =
245+ clientWithRoute Proxy q req baseurl mparam =
380− clientWithRouteAndResultHandler (Proxy :: Proxy sublayout) q t
246+ clientWithRoute (Proxy :: Proxy sublayout) q
381− (req {qParams = paramPair : qParams req}) baseurl opts wrap
247+ (req {qParams = paramPair : qParams req}) baseurl
383 where pname = symbolVal (Proxy :: Proxy sym)
249 where pname = symbolVal (Proxy :: Proxy sym)
384− --p prm = QueryPartParam $ (fmap . fmap) (toQueryParam) prm
250+ p prm = QueryPartParam $ fmap maybeToList $ (fmap . fmap) (unpack . toQueryParam) prm
385− --paramPair = (T.pack pname, p mparam)
251+ paramPair = (pname, p mparam)
386− p prm = QueryPartParam $ fmap qParamToQueryPart prm -- (fmap . fmap) (unpack . toQueryParam) prm
387− paramPair = (T.pack pname, p mparam)
391-- | If you use a 'QueryParams' in one of your endpoints in your API,
253-- | If you use a 'QueryParams' in one of your endpoints in your API,
392-- the corresponding querying function will automatically take
254-- the corresponding querying function will automatically take
@@ -416,22 +278,21 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient t m sublayout tag, Reflex
416-- > -- 'getBooksBy []' for all books
278-- > -- 'getBooksBy []' for all books
417-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
279-- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
418-- > -- to get all books by Asimov and Heinlein
280-- > -- to get all books by Asimov and Heinlein
419−instance (KnownSymbol sym, ToHttpApiData a, HasClient t m sublayout tag, Reflex t)
281+instance (KnownSymbol sym, ToHttpApiData a, HasClient t m sublayout, Reflex t)
420− => HasClient t m (QueryParams sym a :> sublayout) tag where
282+ => HasClient t m (QueryParams sym a :> sublayout) where
422− type Client t m (QueryParams sym a :> sublayout) tag =
284+ type Client t m (QueryParams sym a :> sublayout) =
423− Dynamic t [a] -> Client t m sublayout tag
285+ Behavior t [a] -> Client t m sublayout
425− clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap paramlist =
287+ clientWithRoute Proxy q req baseurl paramlist =
426− clientWithRouteAndResultHandler (Proxy :: Proxy sublayout) q t req' baseurl opts wrap
288+ clientWithRoute (Proxy :: Proxy sublayout) q req' baseurl
428− where req' = req { qParams = (T.pack pname, params') : qParams req }
290+ where req' = req { qParams = (pname, params') : qParams req }
429 pname = symbolVal (Proxy :: Proxy sym)
291 pname = symbolVal (Proxy :: Proxy sym)
430− params' = QueryPartParams $ (fmap . fmap) toQueryParam
292+ params' = QueryPartParam $ (fmap . fmap) (unpack . toQueryParam)
431 paramlist
293 paramlist
435-- | If you use a 'QueryFlag' in one of your endpoints in your API,
296-- | If you use a 'QueryFlag' in one of your endpoints in your API,
436-- the corresponding querying function will automatically take
297-- the corresponding querying function will automatically take
437-- an additional 'Bool' argument.
298-- an additional 'Bool' argument.
@@ -456,42 +317,32 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient t m sublayout tag, Reflex
456-- > -- 'getBooksBy True' to only get _already published_ books
317-- > -- 'getBooksBy True' to only get _already published_ books
458-- TODO Bring back
319-- TODO Bring back
459−instance (KnownSymbol sym, HasClient t m sublayout tag, Reflex t)
320+instance (KnownSymbol sym, HasClient t m sublayout, Reflex t)
460− => HasClient t m (QueryFlag sym :> sublayout) tag where
321+ => HasClient t m (QueryFlag sym :> sublayout) where
462− type Client t m (QueryFlag sym :> sublayout) tag =
323+ type Client t m (QueryFlag sym :> sublayout) =
463− Dynamic t Bool -> Client t m sublayout tag
324+ Behavior t Bool -> Client t m sublayout
465− clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap flag =
326+ clientWithRoute Proxy q req baseurl flag =
466− clientWithRouteAndResultHandler (Proxy :: Proxy sublayout) q t req' baseurl opts wrap
327+ clientWithRoute (Proxy :: Proxy sublayout) q req' baseurl
468 where req' = req { qParams = thisPair : qParams req }
329 where req' = req { qParams = thisPair : qParams req }
469− thisPair = (T.pack pName, QueryPartFlag flag) :: (Text, QueryPart t)
330+ thisPair = (pName, QueryPartFlag flag) :: (String, QueryPart t)
470 pName = symbolVal (Proxy :: Proxy sym)
331 pName = symbolVal (Proxy :: Proxy sym)
334+-- | Pick a 'Method' and specify where the server you want to query is. You get
335+-- back the full `Response`.
337+instance (MonadWidget t m) => HasClient t m Raw where
338+ type Client t m Raw = Behavior t (Maybe XhrRequest)
340+ -> m (Event t XhrResponse)
474−-- | Send a raw 'XhrRequest ()' directly to 'baseurl'
342+ -- clientWithRoute :: Proxy Raw -> Proxy m -> Req -> BaseUrl -> Client t m Raw
475−instance SupportsServantReflex t m => HasClient t m Raw tag where
343+ clientWithRoute p q req baseurl xhrs triggers = do
476− type Client t m Raw tag = Dynamic t (Either Text (XhrRequest ()))
344+ let okReq = fmapMaybe id $ tag xhrs triggers
477− -> Event t tag
345+ performRequestAsync okReq
478− -> m (Event t (ReqResult tag ()))
480− clientWithRouteAndResultHandler _ _ _ _ baseurl _ wrap xhrs triggers = do
482− let xhrs' = liftA2 (\x path -> case x of
484− Right jx -> Right $ jx { _xhrRequest_url = path <> _xhrRequest_url jx }
485− ) xhrs (showBaseUrl <$> baseurl)
486− xhrs'' = attachPromptlyDynWith (flip (,)) xhrs' triggers :: Event t (tag, Either Text (XhrRequest ()))
487− badReq = fmapMaybe (\(t,x) -> either (Just . (t,)) (const Nothing) x) xhrs'' :: Event t (tag, Text)
488− okReq = fmapMaybe (\(t,x) -> either (const Nothing) (Just . (t,)) x) xhrs'' :: Event t (tag, XhrRequest ())
490− resps <- performRequestsAsync okReq
491− wrap $ leftmost [ uncurry RequestFailure <$> badReq
492− , evalResponse (const $ Right ()) <$> resps
496-- | If you use a 'ReqBody' in one of your endpoints in your API,
347-- | If you use a 'ReqBody' in one of your endpoints in your API,
497-- the corresponding querying function will automatically take
348-- the corresponding querying function will automatically take
@@ -513,72 +364,56 @@ instance SupportsServantReflex t m => HasClient t m Raw tag where
513-- > where host = BaseUrl Http "localhost" 8080
364-- > where host = BaseUrl Http "localhost" 8080
514-- > -- then you can just use "addBook" to query that endpoint
365-- > -- then you can just use "addBook" to query that endpoint
516−instance (MimeRender ct a, HasClient t m sublayout tag, Reflex t)
367+instance (MimeRender ct a, HasClient t m sublayout, Reflex t)
517− => HasClient t m (ReqBody (ct ': cts) a :> sublayout) tag where
368+ => HasClient t m (ReqBody (ct ': cts) a :> sublayout) where
519− type Client t m (ReqBody (ct ': cts) a :> sublayout) tag =
370+ type Client t m (ReqBody (ct ': cts) a :> sublayout) =
520− Dynamic t (Either Text a) -> Client t m sublayout tag
371+ Behavior t (Maybe a) -> Client t m sublayout
522− clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap body =
373+ clientWithRoute Proxy q req baseurl body =
523− clientWithRouteAndResultHandler (Proxy :: Proxy sublayout) q t req' baseurl opts wrap
374+ clientWithRoute (Proxy :: Proxy sublayout) q req' baseurl
524 where req' = req { reqBody = bodyBytesCT }
375 where req' = req { reqBody = bodyBytesCT }
525 ctProxy = Proxy :: Proxy ct
376 ctProxy = Proxy :: Proxy ct
526− ctString = T.pack $ show $ contentType ctProxy
377+ ctString = show $ contentType ctProxy
378+ --ctString = BS.unpack . CI.original . M.mainType $ contentType ctProxy
527 bodyBytesCT = Just $ (fmap . fmap)
379 bodyBytesCT = Just $ (fmap . fmap)
528 (\b -> (mimeRender ctProxy b, ctString))
380 (\b -> (mimeRender ctProxy b, ctString))
533-- | Make the querying function append @path@ to the request path.
383-- | Make the querying function append @path@ to the request path.
534−instance (KnownSymbol path, HasClient t m sublayout tag, Reflex t) => HasClient t m (path :> sublayout) tag where
384+instance (KnownSymbol path, HasClient t m sublayout, Reflex t) => HasClient t m (path :> sublayout) where
535− type Client t m (path :> sublayout) tag = Client t m sublayout tag
385+ type Client t m (path :> sublayout) = Client t m sublayout
537− clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap =
387+ clientWithRoute Proxy q req baseurl =
538− clientWithRouteAndResultHandler (Proxy :: Proxy sublayout) q t
388+ clientWithRoute (Proxy :: Proxy sublayout) q
539− (prependToPathParts (pure (Right $ T.pack p)) req) baseurl opts wrap
389+ (prependToPathParts (constant (Just p)) req)
541 where p = symbolVal (Proxy :: Proxy path)
392 where p = symbolVal (Proxy :: Proxy path)
394+instance HasClient t m api => HasClient t m (Vault :> api) where
395+ type Client t m (Vault :> api) = Client t m api
544−instance HasClient t m api tag => HasClient t m (Vault :> api) tag where
397+ clientWithRoute Proxy q req baseurl =
545− type Client t m (Vault :> api) tag = Client t m api tag
398+ clientWithRoute (Proxy :: Proxy api) q req baseurl
547− clientWithRouteAndResultHandler Proxy =
548− clientWithRouteAndResultHandler (Proxy :: Proxy api)
551−instance HasClient t m api tag => HasClient t m (RemoteHost :> api) tag where
552− type Client t m (RemoteHost :> api) tag = Client t m api tag
554− clientWithRouteAndResultHandler Proxy =
555− clientWithRouteAndResultHandler (Proxy :: Proxy api)
400+instance HasClient t m api => HasClient t m (RemoteHost :> api) where
401+ type Client t m (RemoteHost :> api) = Client t m api
559−instance HasClient t m api tag => HasClient t m (IsSecure :> api) tag where
403+ clientWithRoute Proxy q req baseurl =
560− type Client t m (IsSecure :> api) tag = Client t m api tag
404+ clientWithRoute (Proxy :: Proxy api) q req baseurl
562− clientWithRouteAndResultHandler Proxy =
406+instance HasClient t m api => HasClient t m (IsSecure :> api) where
563− clientWithRouteAndResultHandler (Proxy :: Proxy api)
407+ type Client t m (IsSecure :> api) = Client t m api
409+ clientWithRoute Proxy q req baseurl =
410+ clientWithRoute (Proxy :: Proxy api) q req baseurl
566−instance (HasClient t m api tag, Reflex t)
412+instance HasClient t m subapi =>
567− => HasClient t m (BasicAuth realm usr :> api) tag where
413+ HasClient t m (WithNamedContext name config subapi) where
569− type Client t m (BasicAuth realm usr :> api) tag = Dynamic t (Maybe BasicAuthData)
415+ type Client t m (WithNamedContext name config subapi) = Client t m subapi
570− -> Client t m api tag
416+ clientWithRoute Proxy q = clientWithRoute (Proxy :: Proxy subapi) q
572− clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap authdata =
573− clientWithRouteAndResultHandler (Proxy :: Proxy api) q t req' baseurl opts wrap
575− req' = req { authData = Just authdata }
577−-- instance HasClient t m subapi =>
578−-- HasClient t m (WithNamedConfig name config subapi) where
580−-- type Client t m (WithNamedConfig name config subapi) = Client t m subapi
581−-- clientWithRoute Proxy q = clientWithRoute (Proxy :: Proxy subapi) q
584{- Note [Non-Empty Content Types]
419{- Note [Non-Empty Content Types]
@@ -596,30 +431,3 @@ non-empty lists, but is otherwise more specific, no instance will be overall
596more specific. This in turn generally means adding yet another instance (one
431more specific. This in turn generally means adding yet another instance (one
597for empty and one for non-empty lists).
432for 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