on 630a6eb help sign in
5 files changed+318−125
since splitsnapshots
unifiedsplit
exec/Example.hs+20−19 overrides-ghc.nix+18−15 overrides.nix+3−3 src/Servant/Common/Req.hs+169−10 src/Servant/Reflex.hs+108−78
exec/Example.hs+20−19
@@ -30,6 +30,7 @@ api = Proxy
3030
3131main :: IO ()
3232main = mainWidget $ do
33+ el "h1" $ text "Wowzers!"
3334 divClass "example-base" run
3435 divClass "example-multi" runMulti
3536
@@ -64,9 +65,9 @@ runMulti = do
6465 grts <- fmap (fmap T.words . value) $ divClass "" $ do
6566 text "Greetings"
6667 textInput def
67− gust <- fmap (value) $ divClass "gusto-input" $ checkbox False def
68+ gust <- fmap value $ divClass "gusto-input" $ checkbox False def
6869 b <- button "Go"
69− r' <- sayHi (fmap QParamSome <$> nms) (fmap (:[]) $ grts) (constDyn [True, False]) (1 <$ b)
70+ r' <- sayHi (fmap QParamSome <$> nms) (fmap (:[]) grts) (constDyn [True, False]) (1 <$ b)
7071
7172 dynText =<< holdDyn "Waiting" (T.pack . show . catMaybes . fmap reqSuccess <$> r')
7273
@@ -103,11 +104,11 @@ run = mdo
103104 unitResponse <- getUnit $ tag (current reqCount) unitBtn
104105 intResponse :: Event t (ReqResult Int Int) <- getInt $ tag (current reqCount) intBtn
105106
106− score <- foldDyn (+) 0 (fmapMaybe reqSuccess (intResponse))
107+ score <- foldDyn (+) 0 (fmapMaybe reqSuccess intResponse)
107108
108− r <- holdDyn "Waiting" $ fmap showXhrResponse $
109− leftmost [fmapMaybe response (unitResponse)
110− ,fmapMaybe response (intResponse)
109+ r <- holdDyn "Waiting" $ showXhrResponse <$>
110+ leftmost [fmapMaybe response unitResponse
111+ ,fmapMaybe response intResponse
111112 ]
112113 divClass "unit-int-response" $ el "p" $ dynText r >> el "br" (return ()) >> text "Total: " >> display score
113114 return (unitBtn, intBtn)
@@ -116,14 +117,14 @@ run = mdo
116117
117118 text "Name"
118119 el "br" $ return ()
119− inp :: Dynamic t Text <- fmap value $ divClass "name-input" $ (textInput def)
120+ inp :: Dynamic t Text <- fmap value $ divClass "name-input" $ textInput def
120121 let checkedName = fmap (\i -> bool (QParamSome i) (QParamInvalid "Need a name") (T.null i)) inp
121122 el "br" $ return ()
122123
123124 text "Greetings (space-separated)"
124125 el "br" $ return ()
125126 greetings <- fmap (fmap T.words . value) $
126− divClass "greetings-input" $ (textInput def)
127+ divClass "greetings-input" $ textInput def
127128
128129 el "br" $ return ()
129130
@@ -135,23 +136,23 @@ run = mdo
135136
136137 resp <- sayhi checkedName greetings gusto (tag (current reqCount) triggers)
137138 divClass "greeting-response" $ dynText =<<
138− holdDyn "No hi yet" (leftmost [ fmapMaybe reqSuccess (resp)
139− , fmapMaybe reqFailure (resp)])
139+ holdDyn "No hi yet" (leftmost [ fmapMaybe reqSuccess resp
140+ , fmapMaybe reqFailure resp])
140141 return sayHiClicks
141142
142143 dblBtn <- elClass "div" "demo-group" $ do
143144 text "A Double to double"
144145 el "br" $ return ()
145146 dblinp <- fmap value $ divClass "double-input" $ textInput def
146− (dblBtn) <- divClass "double-button" $ button "Double it"
147− dblResp <- dbl (fmap (note "read failure" . readMaybe . T.unpack) $
147+ dblBtn <- divClass "double-button" $ button "Double it"
148+ dblResp <- dbl ((note "read failure" . readMaybe . T.unpack) <$>
148149 dblinp) (tag (current reqCount) dblBtn)
149150 divClass "double-errors" $ dynText =<<
150− holdDyn "(no errors)" (fmapMaybe reqFailure (dblResp))
151+ holdDyn "(no errors)" (fmapMaybe reqFailure dblResp)
151152 el "br" (return ())
152153 divClass "double-result" $ el "p" $ dynText =<<
153− holdDyn "No number yet" (fmap tShow $
154− fmapMaybe reqSuccess (dblResp))
154+ holdDyn "No number yet" (tShow <$>
155+ fmapMaybe reqSuccess dblResp)
155156 return dblBtn
156157
157158 mpGo <- elClass "div" "demo-group" $ do
@@ -159,9 +160,9 @@ run = mdo
159160 b <- value <$> checkbox False def
160161 mpGo <- button "Test"
161162 multiResp <- multi b (tag (current reqCount) mpGo)
162− dynText =<< holdDyn "No res yet" (fmap tShow $
163− fmapMaybe reqSuccess $
164− (multiResp))
163+ dynText =<< holdDyn "No res yet" (tShow <$>
164+ fmapMaybe reqSuccess
165+ multiResp)
165166 return mpGo
166167
167168 return ()
@@ -171,7 +172,7 @@ run = mdo
171172 elClass "div" "demo-group" $ do
172173 text "JSON Unicode encoding test"
173174 txt <- value <$> textInput def
174− ev <- fmap (1 <$) $ button "Question"
175+ ev <- (1 <$) <$> button "Question"
175176 let dQ = Right . Question <$> traceDyn "will send: " txt
176177 rr <- qna dQ ev
177178 el "p" $
overrides-ghc.nix+18−15
@@ -2,18 +2,21 @@
22let
33 dc = reflex-platform.lib.dontCheck;
44 c2n = reflex-platform.cabal2nixResult;
5−in reflex-platform.ghc.override {
6− overrides = self: super: {
7− servant-snap = dc (self.callPackage (c2n deps/servant-snap) {});
8− snap = dc (self.callPackage (c2n deps/servant-snap/deps/snap) {});
9− snap-server = dc (self.callPackage (c2n deps/servant-snap/deps/snap/deps/snap-server) {});
10− io-streams = dc (self.callPackage (c2n deps/servant-snap/deps/snap/deps/io-streams) {});
11− io-streams-haproxy = dc (self.callPackage (c2n deps/servant-snap/deps/snap/deps/io-streams-haproxy) {});
12− heist = dc (self.callPackage (c2n deps/servant-snap/deps/snap/deps/heist) {});
13− xmlhtml = dc (self.callPackage (c2n deps/servant-snap/deps/snap/deps/xmlhtml) {});
14− snap-core = dc (self.callPackage (c2n deps/servant-snap/deps/snap/deps/snap-core) {});
15− servant = dc (self.callPackage (c2n deps/servant/servant) {});
16− Glob = dc super.Glob;
17− http-api-data = dc (self.callPackage (c2n deps/http-api-data) {});
18− };
19−}
5+ ghc-mod = (import <nixpkgs> {}).haskellPackages.ghc-mod;
6+in
7+ reflex-platform.ghc.override {
8+ overrides = self: super: {
9+ servant-snap = dc (self.callPackage (c2n deps/servant-snap) {});
10+ snap = dc (self.callPackage (c2n deps/servant-snap/deps/snap) {});
11+ snap-server = dc (self.callPackage (c2n deps/servant-snap/deps/snap/deps/snap-server) {});
12+ io-streams = dc (self.callPackage (c2n deps/servant-snap/deps/snap/deps/io-streams) {});
13+ io-streams-haproxy = dc (self.callPackage (c2n deps/servant-snap/deps/snap/deps/io-streams-haproxy) {});
14+ heist = dc (self.callPackage (c2n deps/servant-snap/deps/snap/deps/heist) {});
15+ xmlhtml = dc (self.callPackage (c2n deps/servant-snap/deps/snap/deps/xmlhtml) {});
16+ snap-core = dc (self.callPackage (c2n deps/servant-snap/deps/snap/deps/snap-core) {});
17+ servant = dc (self.callPackage (c2n deps/servant/servant) {});
18+ Glob = dc super.Glob;
19+ http-api-data = dc (self.callPackage (c2n deps/http-api-data) {});
20+ ghc-mod = ghc-mod;
21+ };
22+ }
overrides.nix+3−3
@@ -6,8 +6,8 @@ let
66
77in
88reflex-platform.ghcjs.override {
9− overrides = self: super: {
10− # servant = dc (self.callPackage (c2n deps/servant/servant) {});
11− # http-api-data = dc (self.callPackage (c2n deps/http-api-data) {});
9+ overrides = self: super: {
10+ servant = dc (self.callPackage (c2n deps/servant/servant) {});
11+ http-api-data = dc (self.callPackage (c2n deps/http-api-data) {});
1212 };
1313}
src/Servant/Common/Req.hs+169−10
@@ -26,6 +26,7 @@ import qualified Data.Text as T
2626import qualified Data.Text.Encoding as TE
2727import Data.Traversable (forM)
2828import Reflex.Dom hiding (tag)
29+import Reflex.Dom.Xhr (newXMLHttpRequest)
2930import Servant.Common.BaseUrl (BaseUrl, showBaseUrl,
3031 SupportsServantReflex)
3132import Servant.API.ContentTypes (MimeUnrender(..), NoContent(..))
@@ -107,6 +108,10 @@ data QueryPart t = QueryPartParam (Dynamic t (Either Text (Maybe Text)))
107108 | QueryPartParams (Dynamic t [Text])
108109 | QueryPartFlag (Dynamic t Bool)
109110
111+data QueryPartIO = QueryPartParamIO (Maybe Text)
112+ | QueryPartParamsIO [Text]
113+ | QueryPartFlagIO Bool
114+
110115
111116-------------------------------------------------------------------------------
112117-- The data structure used to build up request information while traversing
@@ -121,6 +126,16 @@ data Req t = Req
121126 , authData :: Maybe (Dynamic t (Maybe BasicAuthData))
122127 }
123128
129+data ReqIO = ReqIO
130+ { reqMethodIO :: Text
131+ , reqPathPartsIO :: [Text]
132+ , qParamsIO :: [(Text, QueryPartIO)]
133+ , reqBodyIO :: Maybe (BL.ByteString, Text)
134+ , headersIO :: [(Text, Text)]
135+ , respHeadersIO :: XhrResponseHeaders
136+ , authDataIO :: Maybe BasicAuthData
137+ }
138+
124139defReq :: Req t
125140defReq = Req "GET" [] [] Nothing [] def Nothing
126141
@@ -137,7 +152,7 @@ reqToReflexRequest
137152 => Text
138153 -> Dynamic t BaseUrl
139154 -> Req t
140− -> (Dynamic t (Either Text (XhrRequest XhrPayload)))
155+ -> Dynamic t (Either Text (XhrRequest XhrPayload))
141156reqToReflexRequest reqMeth reqHost req =
142157 let t :: Dynamic t [Either Text Text]
143158 t = sequence $ reverse $ reqPathParts req
@@ -168,7 +183,9 @@ reqToReflexRequest reqMeth reqHost req =
168183
169184 queryPartStrings :: [Dynamic t (Maybe (Either Text Text))]
170185 queryPartStrings = map queryPartString (qParams req)
171− queryPartStrings' = fmap (sequence . catMaybes) $ sequence queryPartStrings :: Dynamic t (Either Text [Text])
186+
187+ queryPartStrings' :: Dynamic t (Either Text [Text])
188+ queryPartStrings' = sequence . catMaybes <$> sequence queryPartStrings :: Dynamic t (Either Text [Text])
172189 queryString :: Dynamic t (Either Text Text) =
173190 ffor queryPartStrings' $ \qs -> fmap (T.intercalate "&") qs
174191 xhrUrl = (liftA3 . liftA3) (\a p q -> a </> if T.null q then p else p <> "?" <> q)
@@ -182,17 +199,17 @@ reqToReflexRequest reqMeth reqHost req =
182199 xhrHeaders :: Dynamic t (Either Text [(Text, Text)])
183200 xhrHeaders = (fmap sequence . sequence . fmap f . headers) req
184201 where
185− f = \(headerName, dynam) ->
202+ f (headerName, dynam) =
186203 fmap (fmap (\rightVal -> (headerName, rightVal))) dynam
187204
188205 mkConfigBody :: Either Text [(Text,Text)]
189− -> (Either Text (BL.ByteString, Text))
206+ -> Either Text (BL.ByteString, Text)
190207 -> Either Text (XhrRequestConfig XhrPayload)
191208 mkConfigBody ehs rb = case (ehs, rb) of
192209 (_, Left e) -> Left e
193210 (Left e, _) -> Left e
194211 (Right hs, Right (bBytes, bCT)) ->
195− Right $ XhrRequestConfig
212+ Right XhrRequestConfig
196213 { _xhrRequestConfig_sendData = bytesToPayload bBytes
197214 , _xhrRequestConfig_headers =
198215 Map.insert "Content-Type" bCT (Map.fromList hs)
@@ -229,10 +246,103 @@ reqToReflexRequest reqMeth reqHost req =
229246 Nothing -> xhr
230247 Just auth -> liftA2 mkAuth auth xhr
231248
232− xhrReq = (liftA2 . liftA2) (\p opt -> XhrRequest reqMeth p opt) xhrUrl (addAuth xhrOpts)
249+ xhrReq = (liftA2 . liftA2) (XhrRequest reqMeth) xhrUrl (addAuth xhrOpts)
233250
234251 in xhrReq
235252
253+reqToReflexRequestIO
254+ :: Text
255+ -> BaseUrl
256+ -> ReqIO
257+ -> XhrRequest XhrPayload
258+reqToReflexRequestIO reqMeth reqHost req =
259+ let t :: [Text]
260+ t = reverse $ reqPathPartsIO req
261+
262+ baseUrl :: Text
263+ baseUrl = showBaseUrl reqHost
264+
265+ urlParts :: [Text]
266+ urlParts = t
267+
268+ urlPath :: Text
269+ urlPath = T.intercalate "/" urlParts
270+
271+ queryPartString :: (Text, QueryPartIO) -> Maybe Text
272+ queryPartString (pName, qp) = case qp of
273+ QueryPartParamIO p -> case p of
274+ Just a -> Just (pName <> "=" <> a)
275+ _ -> Nothing
276+ QueryPartParamsIO ps ->
277+ if null ps
278+ then Nothing
279+ else Just (T.intercalate "&" (fmap (\p -> pName <> "=" <> p) ps))
280+ QueryPartFlagIO fl -> if fl
281+ then Just pName
282+ else Nothing
283+
284+
285+ queryPartStrings :: [Maybe Text]
286+ queryPartStrings = map queryPartString (qParamsIO req)
287+
288+ queryPartStrings' :: [Text]
289+ queryPartStrings' = catMaybes queryPartStrings
290+
291+ queryString :: Text = T.intercalate "&" queryPartStrings'
292+
293+ xhrUrl = (\a p q -> a </> if T.null q then p else p <> "?" <> q)
294+ baseUrl urlPath queryString
295+ where
296+ (</>) :: Text -> Text -> Text
297+ x </> y | ("/" `T.isSuffixOf` x) || ("/" `T.isPrefixOf` y) = x <> y
298+ | otherwise = x <> "/" <> y
299+
300+
301+ xhrHeaders :: [(Text, Text)]
302+ xhrHeaders = headersIO req
303+
304+ mkConfigBody :: [(Text,Text)]
305+ -> (BL.ByteString, Text)
306+ -> XhrRequestConfig XhrPayload
307+ mkConfigBody hs (bBytes, bCT) =
308+ XhrRequestConfig
309+ { _xhrRequestConfig_sendData = bytesToPayload bBytes
310+ , _xhrRequestConfig_headers =
311+ Map.insert "Content-Type" bCT (Map.fromList hs)
312+ , _xhrRequestConfig_user = Nothing
313+ , _xhrRequestConfig_password = Nothing
314+ , _xhrRequestConfig_responseType = Nothing
315+ , _xhrRequestConfig_withCredentials = False
316+ , _xhrRequestConfig_responseHeaders = def
317+ }
318+
319+ xhrOpts :: XhrRequestConfig XhrPayload
320+ xhrOpts = case reqBodyIO req of
321+ Nothing -> def { _xhrRequestConfig_headers = Map.fromList xhrHeaders
322+ , _xhrRequestConfig_user = Nothing
323+ , _xhrRequestConfig_password = Nothing
324+ , _xhrRequestConfig_responseType = Nothing
325+ , _xhrRequestConfig_sendData = ""
326+ , _xhrRequestConfig_withCredentials = False
327+ }
328+ Just rBody -> mkConfigBody xhrHeaders rBody
329+
330+ mkAuth :: BasicAuthData -> XhrRequestConfig x -> XhrRequestConfig x
331+ mkAuth (BasicAuthData u p) config = config
332+ { _xhrRequestConfig_user = Just $ TE.decodeUtf8 u
333+ , _xhrRequestConfig_password = Just $ TE.decodeUtf8 p}
334+
335+ addAuth :: XhrRequestConfig x
336+ -> XhrRequestConfig x
337+ addAuth xhr = case authDataIO req of
338+ Nothing -> xhr
339+ Just auth -> mkAuth auth xhr
340+
341+ xhrReq = (XhrRequest reqMeth) xhrUrl (addAuth xhrOpts)
342+
343+ in xhrReq
344+
345+
236346-- * performing requests
237347
238348displayHttpRequest :: Text -> Text
@@ -275,9 +385,8 @@ performSomeRequestsAsync'
275385performSomeRequestsAsync' newXhr req = performEventAsync $ ffor req $ \hrs cb -> do
276386 rs <- hrs
277387 resps <- forM rs $ \r -> case r of
278− Left e -> do
279− resp <- liftIO $ newMVar (Left e)
280− return resp
388+ Left e -> liftIO $ newMVar (Left e)
389+
281390 Right r' -> do
282391 resp <- liftIO newEmptyMVar
283392 _ <- newXhr r' $ liftIO . putMVar resp . Right
@@ -290,7 +399,7 @@ performSomeRequestsAsync' newXhr req = performEventAsync $ ffor req $ \hrs cb ->
290399-- | This function actually performs the request.
291400performRequest :: forall t m tag .(SupportsServantReflex t m)
292401 => Text
293− -> (Req t)
402+ -> Req t
294403 -> Dynamic t BaseUrl
295404 -> Event t tag
296405 -> m (Event t (tag, XhrResponse), Event t (tag, Text))
@@ -306,6 +415,32 @@ performRequest reqMeth req reqHost trigger = do
306415 return (resps, badReqs)
307416
308417
418+-- | This function actually performs the request.
419+performRequestIO :: forall t m tag. (SupportsServantReflex t m)
420+ => Text
421+ -> ReqIO
422+ -> BaseUrl
423+ -> tag
424+ -> m (Either (tag, XhrResponse) (tag, Text))
425+performRequestIO reqMeth req reqHost trigger = do
426+
427+ let xhrReq = reqToReflexRequestIO reqMeth reqHost req
428+ let reqs = (trigger ,xhrReq )
429+
430+ -- resps <- performRequestsAsync okReqs
431+
432+ -- let x :: _ = newXMLHttpRequestWithError
433+
434+ -- return (resps, badReqs)
435+ undefined
436+
437+doThing :: (IsXhrPayload a, HasWebView IO) => XhrRequest a -> IO XhrResponse
438+doThing req = do
439+ v <- newEmptyMVar
440+ newXMLHttpRequest req $ putMVar v
441+ takeMVar v
442+
443+
309444type XhrPayload = T.Text
310445bytesToPayload :: BL.ByteString -> XhrPayload
311446bytesToPayload = TE.decodeUtf8 . BL.toStrict
@@ -370,6 +505,30 @@ performRequestsCT ct reqMeth reqs reqHost trigger = do
370505 resps
371506
372507
508+performRequestsCTIO
509+ :: (SupportsServantReflex t m,
510+ MimeUnrender ct a, Traversable f)
511+ => Proxy ct
512+ -> Text
513+ -> Dynamic t (f (Req t))
514+ -> Dynamic t BaseUrl
515+ -> Event t tag
516+ -> m (Event t (f (ReqResult tag a)))
517+performRequestsCTIO ct reqMeth reqs reqHost trigger = do
518+ resps <- performRequests reqMeth reqs reqHost trigger
519+ let decodeResp x = first T.pack .
520+ mimeUnrender ct .
521+ BL.fromStrict .
522+ TE.encodeUtf8 =<< note "No body text"
523+ (_xhrResponse_responseText x)
524+ return $ fmap
525+ (\(t,rs) -> ffor rs $ \r -> case r of
526+ Left e -> RequestFailure t e
527+ Right g -> evalResponse decodeResp (t,g)
528+ )
529+ resps
530+
531+
373532performRequestsNoBody
374533 :: (SupportsServantReflex t m,
375534 Traversable f)
src/Servant/Reflex.hs+108−78
@@ -1,24 +1,18 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE FlexibleContexts #-}
5−{-# LANGUAGE FlexibleInstances #-}
6−{-# 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 #-}
15−{-# LANGUAGE OverloadedStrings #-}
16−{-# LANGUAGE PolyKinds #-}
17−{-# LANGUAGE ScopedTypeVariables #-}
18−{-# LANGUAGE TupleSections #-}
19−{-# LANGUAGE TypeFamilies #-}
20−{-# LANGUAGE TypeOperators #-}
21−{-# LANGUAGE UndecidableInstances #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE CPP #-}
3+{-# LANGUAGE DataKinds #-}
4+{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE FunctionalDependencies #-}
7+{-# LANGUAGE InstanceSigs #-}
8+{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE OverloadedStrings #-}
10+{-# LANGUAGE PolyKinds #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TupleSections #-}
13+{-# LANGUAGE TypeFamilies #-}
14+{-# LANGUAGE TypeOperators #-}
15+{-# LANGUAGE UndecidableInstances #-}
2216
2317-- #include "overlapping-compat.h"
2418-- | This module provides 'client' which can automatically generate
@@ -35,49 +29,46 @@ module Servant.Reflex
3529
3630------------------------------------------------------------------------------
3731import Control.Applicative
38−import Data.Monoid ((<>))
39−import qualified Data.Set as Set
40−import qualified Data.Text.Encoding as E
41−import Data.CaseInsensitive (mk)
32+import Data.CaseInsensitive (mk)
4233import Data.Functor.Identity
43−import Data.Proxy (Proxy (..))
44−import qualified Data.Map as Map
45−import Data.Text (Text)
46−import qualified Data.Text as T
47−import GHC.TypeLits (KnownSymbol, symbolVal)
48−import Servant.API ((:<|>)(..),(:>), BasicAuth,
49− BasicAuthData, BuildHeadersTo(..),
50− Capture, contentType, Header,
51− Headers(..), HttpVersion, IsSecure,
52− MimeRender(..), MimeUnrender,
53− NoContent, QueryFlag, QueryParam,
54− QueryParams, Raw, ReflectMethod(..),
55− RemoteHost, ReqBody,
56− ToHttpApiData(..), Vault, Verb)
57−
58−import Reflex.Dom (Dynamic, Event, Reflex,
59− XhrRequest(..),
60− XhrResponseHeaders(..),
61− XhrResponse(..), attachPromptlyDynWith, constDyn, ffor, fmapMaybe,
62− leftmost, performRequestsAsync,
63− )
34+import qualified Data.Map as Map
35+import Data.Monoid ((<>))
36+import Data.Proxy (Proxy (..))
37+import qualified Data.Set as Set
38+import Data.Text (Text)
39+import qualified Data.Text as T
40+import qualified Data.Text.Encoding as E
41+import GHC.TypeLits (KnownSymbol, symbolVal)
42+import Servant.API ((:<|>) (..), (:>), BasicAuth,
43+ BasicAuthData, BuildHeadersTo (..),
44+ Capture, Header, Headers (..),
45+ HttpVersion, IsSecure, MimeRender (..),
46+ MimeUnrender, NoContent, QueryFlag,
47+ QueryParam, QueryParams, Raw,
48+ ReflectMethod (..), RemoteHost,
49+ ReqBody, ToHttpApiData (..), Vault,
50+ Verb, contentType)
51+
52+import Reflex.Dom (Dynamic, Event, Reflex,
53+ XhrRequest (..), XhrResponse (..),
54+ XhrResponseHeaders (..),
55+ attachPromptlyDynWith, constDyn, ffor,
56+ fmapMaybe, leftmost,
57+ performRequestsAsync)
6458------------------------------------------------------------------------------
65−import Servant.Common.BaseUrl (BaseUrl(..), Scheme(..), baseUrlWidget,
66− showBaseUrl,
67− SupportsServantReflex)
68−import Servant.Common.Req (Req, ReqResult(..), QParam(..),
69− QueryPart(..), addHeader, authData,
70− defReq, evalResponse, prependToPathParts,
71− -- performRequestCT,
72− performRequestsCT,
73− -- performRequestNoBody,
74− performRequestsNoBody,
75− performSomeRequestsAsync,
76− qParamToQueryPart, reqBody,
77− reqSuccess, reqFailure,
78− reqMethod, respHeaders, response,
79− reqTag,
80− qParams)
59+import Servant.Common.BaseUrl (BaseUrl (..), Scheme (..),
60+ SupportsServantReflex, baseUrlWidget,
61+ showBaseUrl)
62+import Servant.Common.Req (QParam (..), QueryPart (..), Req,
63+ ReqIO, ReqResult (..), addHeader,
64+ authData, defReq, evalResponse,
65+ performRequestsCT,
66+ performRequestsNoBody,
67+ performSomeRequestsAsync,
68+ prependToPathParts, qParamToQueryPart,
69+ qParams, reqBody, reqFailure,
70+ reqMethod, reqSuccess, reqTag,
71+ respHeaders, response)
8172
8273
8374-- * Accessing APIs as a Client
@@ -97,24 +88,42 @@ import Servant.Common.Req (Req, ReqResult(..), QParam(..),
9788-- > where host = constDyn $ BaseUrl Http "localhost" 8080
9889client :: (HasClient t m layout tag)
9990 => Proxy layout -> Proxy m -> Proxy tag -> Dynamic t BaseUrl -> Client t m layout tag
100−client p q t baseurl = clientWithRoute p q t defReq baseurl
91+client p q t = clientWithRoute p q t defReq
10192
10293
10394-- | This class lets us define how each API combinator
10495-- influences the creation of an HTTP request. It's mostly
10596-- an internal class, you can just use 'client'.
106−class HasClient t m layout (tag :: *) where
97+class HasClient t m layout (tag :: *) | m -> t where
10798 type Client t m layout tag :: *
108− clientWithRoute :: Proxy layout -> Proxy m -> Proxy tag -> Req t -> Dynamic t BaseUrl -> Client t m layout tag
109−
99+ type ClientIO m layout tag :: *
100+ clientWithRoute
101+ :: Proxy layout
102+ -> Proxy m
103+ -> Proxy tag
104+ -> Req t
105+ -> Dynamic t BaseUrl
106+ -> Client t m layout tag
107+ clientWithRouteIO
108+ :: Proxy layout
109+ -> Proxy m
110+ -> Proxy tag
111+ -> ReqIO
112+ -> BaseUrl
113+ -> ClientIO m layout tag
110114
111115instance (HasClient t m a tag, HasClient t m b tag) => HasClient t m (a :<|> b) tag where
112116 type Client t m (a :<|> b) tag = Client t m a tag :<|> Client t m b tag
117+ type ClientIO m (a :<|> b) tag = ClientIO m a tag :<|> ClientIO m b tag
113118
114119 clientWithRoute Proxy q pTag req baseurl =
115120 clientWithRoute (Proxy :: Proxy a) q pTag req baseurl :<|>
116121 clientWithRoute (Proxy :: Proxy b) q pTag req baseurl
117122
123+ clientWithRouteIO Proxy q pTag req baseurl =
124+ clientWithRouteIO (Proxy :: Proxy a) q pTag req baseurl :<|>
125+ clientWithRouteIO (Proxy :: Proxy b) q pTag req baseurl
126+
118127
119128-- Capture. Example:
120129-- > type MyApi = "books" :> Capture "isbn" Text :> Get '[JSON] Book
@@ -134,12 +143,22 @@ instance (SupportsServantReflex t m, ToHttpApiData a, HasClient t m sublayout ta
134143 type Client t m (Capture capture a :> sublayout) tag =
135144 Dynamic t (Either Text a) -> Client t m sublayout tag
136145
146+ type ClientIO m (Capture capture a :> sublayout) tag =
147+ a -> ClientIO m sublayout tag
148+
137149 clientWithRoute Proxy q t req baseurl val =
138150 clientWithRoute (Proxy :: Proxy sublayout)
139151 q t
140152 (prependToPathParts p req)
141153 baseurl
142− where p = (fmap . fmap) (toUrlPiece) val
154+ where p = (fmap . fmap) toUrlPiece val
155+
156+ clientWithRouteIO Proxy q t req baseurl val =
157+ clientWithRouteIO (Proxy :: Proxy sublayout)
158+ q t
159+ (prependToPathParts p req)
160+ baseurl
161+ where p = (fmap . fmap) toUrlPiece val
143162
144163
145164-- VERB (Returning content) --
@@ -147,13 +166,24 @@ instance {-# OVERLAPPABLE #-}
147166 -- Note [Non-Empty Content Types]
148167 (MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts), SupportsServantReflex t m
149168 ) => HasClient t m (Verb method status cts' a) tag where
169+
150170 type Client t m (Verb method status cts' a) tag =
151171 Event t tag -> m (Event t (ReqResult tag a))
172+
173+ type ClientIO m (Verb method status cts' a) tag =
174+ tag -> m (ReqResult tag a)
175+
152176 -- TODO how to access input types here?
153177 -- ExceptT ServantError IO a
154178 clientWithRoute Proxy _ _ req baseurl trigs =
155179 -- performRequestCT (Proxy :: Proxy ct) method req' baseurl trigs
156− fmap runIdentity <$> performRequestsCT (Proxy :: Proxy ct) method (constDyn $ Identity $ req') baseurl trigs
180+ fmap runIdentity <$> performRequestsCT (Proxy :: Proxy ct) method (constDyn $ Identity req') baseurl trigs
181+ where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
182+ req' = req { reqMethod = method }
183+
184+ clientWithRouteIO Proxy _ _ req baseurl tag =
185+ -- performRequestCT (Proxy :: Proxy ct) method req' baseurl trigs
186+ fmap runIdentity <$> performRequestsCT (Proxy :: Proxy ct) method (constDyn $ Identity req') baseurl tag
157187 where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
158188 req' = req { reqMethod = method }
159189
@@ -267,8 +297,8 @@ instance HasClient t m sublayout tag
267297 type Client t m (HttpVersion :> sublayout) tag =
268298 Client t m sublayout tag
269299
270− clientWithRoute Proxy q t =
271− clientWithRoute (Proxy :: Proxy sublayout) q t
300+ clientWithRoute Proxy =
301+ clientWithRoute (Proxy :: Proxy sublayout)
272302
273303
274304-- | If you use a 'QueryParam' in one of your endpoints in your API,
@@ -462,9 +492,9 @@ instance (MimeRender ct a, HasClient t m sublayout tag, Reflex t)
462492instance (KnownSymbol path, HasClient t m sublayout tag, Reflex t) => HasClient t m (path :> sublayout) tag where
463493 type Client t m (path :> sublayout) tag = Client t m sublayout tag
464494
465− clientWithRoute Proxy q t req baseurl =
495+ clientWithRoute Proxy q t req =
466496 clientWithRoute (Proxy :: Proxy sublayout) q t
467− (prependToPathParts (pure (Right $ T.pack p)) req) baseurl
497+ (prependToPathParts (pure (Right $ T.pack p)) req)
468498
469499 where p = symbolVal (Proxy :: Proxy path)
470500
@@ -472,23 +502,23 @@ instance (KnownSymbol path, HasClient t m sublayout tag, Reflex t) => HasClient
472502instance HasClient t m api tag => HasClient t m (Vault :> api) tag where
473503 type Client t m (Vault :> api) tag = Client t m api tag
474504
475− clientWithRoute Proxy q t req baseurl =
476− clientWithRoute (Proxy :: Proxy api) q t req baseurl
505+ clientWithRoute Proxy =
506+ clientWithRoute (Proxy :: Proxy api)
477507
478508
479509instance HasClient t m api tag => HasClient t m (RemoteHost :> api) tag where
480510 type Client t m (RemoteHost :> api) tag = Client t m api tag
481511
482− clientWithRoute Proxy q t req baseurl =
483− clientWithRoute (Proxy :: Proxy api) q t req baseurl
512+ clientWithRoute Proxy =
513+ clientWithRoute (Proxy :: Proxy api)
484514
485515
486516
487517instance HasClient t m api tag => HasClient t m (IsSecure :> api) tag where
488518 type Client t m (IsSecure :> api) tag = Client t m api tag
489519
490− clientWithRoute Proxy q t req baseurl =
491− clientWithRoute (Proxy :: Proxy api) q t req baseurl
520+ clientWithRoute Proxy =
521+ clientWithRoute (Proxy :: Proxy api)
492522
493523
494524instance (HasClient t m api tag, Reflex t)