git.iko.soy/typeable/servant-reflex
on master help sign in
Add some Event Tuple instances toward tuple api
commita00c3f880eb2a89b4d07523cd261645d67e548f3 authorGreg Hale <imalsogreg@gmail.com> dateMon 29 May 2017, 23:40 (9 years ago) parent630a6eb
16 files changed+399−94
.gitignore−1 .gitmodules+3−6 .travis.yml+2−3 build.sh+1−1 deps/reflex−1 deps/reflex-dom−1 deps/reflex-platform+1 exec/Example.hs+3−1 exec/TupleExample.hs+35 overrides-ghc.nix+8−19 overrides.nix+1−5 servant-reflex.cabal+5−2 src/Servant/Common/BaseUrl.hs+3−2 src/Servant/Common/Req.hs+84−36 src/Servant/Reflex.hs+53−16 src/Servant/Reflex/Tuple.hs+200
.gitignore−1
@@ -6,5 +6,4 @@ log
66*.webapp
77*.stats
88out
9−reflex-platform
109/.stack-work/
.gitmodules+3−6
@@ -1,9 +1,3 @@
1−[submodule "deps/reflex-dom"]
2− path = deps/reflex-dom
3− url = https://github.com/reflex-frp/reflex-dom.git
4−[submodule "deps/reflex"]
5− path = deps/reflex
6− url = https://github.com/reflex-frp/reflex.git
71[submodule "deps/http-api-data"]
82 path = deps/http-api-data
93 url = https://github.com/fizruk/http-api-data
@@ -13,3 +7,6 @@
137[submodule "deps/servant-snap"]
148 path = deps/servant-snap
159 url = https://github.com/haskell-servant/servant-snap
10+[submodule "deps/reflex-platform"]
11+ path = deps/reflex-platform
12+ url = https://github.com/reflex-frp/reflex-platform
.travis.yml+2−3
@@ -35,10 +35,9 @@ install:
3535 $HOME/.cabal/packages/hackage.haskell.org/00-index.tar;
3636 fi
3737 - travis_retry cabal update -v
38− - git clone https://github.com/reflex-frp/reflex-platform
3938 - sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
40− - reflex-platform/work-on ./overrides-ghc.nix ./testserver --command "cd testserver && cabal configure -fExample && cabal build"
41− - reflex-platform/work-on ghc ./testdriver --command "cd testdriver && cabal configure && cabal build"
39+ - deps/reflex-platform/work-on ./overrides-ghc.nix ./testserver --command "cd testserver && cabal configure -fExample && cabal build"
40+ - deps/reflex-platform/work-on ghc ./testdriver --command "cd testdriver && cabal configure && cabal build"
4241 - cd testserver && dist/build/back/back -p 8000 &
4342 - sleep 3
4443 - phantomjs --webdriver=127.0.0.1:4444 &
build.sh+1−1
@@ -1,3 +1,3 @@
11#!/usr/bin/env bash
22
3−reflex-platform/work-on ./overrides.nix ./. --run "cabal configure --ghcjs && cabal build && exec/toSite.sh"
3+deps/reflex-platform/work-on ./overrides.nix ./. --run "cabal configure --ghcjs && cabal build && exec/toSite.sh"
deps/reflexdeleted−1

Submodule changed.

deps/reflex-domdeleted−1

Submodule changed.

deps/reflex-platformnew file+1

Submodule changed.

exec/Example.hs+3−1
@@ -19,10 +19,11 @@ import Servant.API
1919import API
2020import Data.Proxy
2121import Text.Read (readMaybe)
22−import Reflex.Dom
22+import Reflex.Dom hiding (run)
2323------------------------------------------------------------------------------
2424import Servant.Reflex
2525import Servant.Reflex.Multi
26+import qualified TupleExample
2627
2728
2829api :: Proxy API
@@ -33,6 +34,7 @@ main = mainWidget $ do
3334 el "h1" $ text "Wowzers!"
3435 divClass "example-base" run
3536 divClass "example-multi" runMulti
37+ divClass "example-tuple" TupleExample.run
3638
3739
3840runMulti :: forall t m. (SupportsServantReflex t m,
exec/TupleExample.hsnew file+35
@@ -0,0 +1,35 @@
1+{-# LANGUAGE RankNTypes #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE OverloadedStrings #-}
5+{-# LANGUAGE TypeOperators #-}
6+{-# LANGUAGE ScopedTypeVariables #-}
7+{-# LANGUAGE TypeApplications #-}
8+
9+module TupleExample where
10+
11+import Data.Aeson
12+import Data.Proxy
13+import Data.Text
14+import GHC.TypeLits
15+import Reflex.Dom
16+import Servant.API
17+import Servant.Reflex.Tuple
18+
19+type TestAPI = Get '[JSON] Int
20+ -- :<|> Post '[JSON] Int
21+ :<|> Capture "name" Text :> Post '[JSON] Text
22+
23+
24+run :: forall t m. (SupportsServantReflex t m, MonadWidget t m) => m ()
25+run = do
26+ let (getInt :<|> greet) = client (Proxy @TestAPI) (Proxy @m) (BasePath "/")
27+ -- let getInt = client (Proxy @TestAPI) (Proxy @m) (BasePath "/")
28+ b <- button "Go for it"
29+ g <- ("greg" <$) <$> button "Hello"
30+ -- g <- button "Hello"
31+ b' <- getInt b
32+ display =<< holdDyn (Left "Waiting") b'
33+ g' <- greet g
34+ display =<< holdDyn (Left "Waiting") g'
35+
overrides-ghc.nix+8−19
@@ -1,22 +1,11 @@
11{ reflex-platform, ... }:
22let
3− dc = reflex-platform.lib.dontCheck;
43 c2n = reflex-platform.cabal2nixResult;
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− }
4+ dc = reflex-platform.lib.dontCheck;
5+in reflex-platform.ghc.override {
6+ overrides = self: super: {
7+ servant-snap = dc (self.callPackage (c2n deps/servant-snap) {});
8+ heist = dc (self.callPackage (c2n deps/servant-snap/deps/snap/deps/heist) {});
9+ xmlhtml = dc (self.callPackage (c2n deps/servant-snap/deps/snap/deps/xmlhtml) {});
10+ };
11+}
overrides.nix+1−5
@@ -1,13 +1,9 @@
11{ reflex-platform, ... }:
22let
3−
43 dc = reflex-platform.lib.dontCheck;
54 c2n = reflex-platform.cabal2nixResult;
6−
75in
86reflex-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) {});
7+ overrides = self: super: {
128 };
139}
servant-reflex.cabal+5−2
@@ -1,5 +1,5 @@
11Name: servant-reflex
2−Version: 0.3
2+Version: 0.3.1
33Synopsis: Servant reflex API generator
44Description: Servant reflex API generator
55License: AllRightsReserved
@@ -19,6 +19,7 @@ library
1919 exposed-modules:
2020 Servant.Reflex
2121 Servant.Reflex.Multi
22+ Servant.Reflex.Tuple
2223
2324 other-modules:
2425 Servant.Common.BaseUrl
@@ -32,9 +33,10 @@ library
3233 containers >= 0.5.6 && < 0.6,
3334 data-default >= 0.5 && < 0.8,
3435 exceptions >= 0.8 && < 0.9,
35− ghcjs-dom >= 0.2 && < 0.3,
36+ ghcjs-dom >= 0.2 && < 0.10,
3637 http-api-data >= 0.2 && < 0.4,
3738 http-media >= 0.6 && < 0.7,
39+ jsaddle >= 0.8 && < 0.10,
3840 mtl >= 2.2.1 && < 2.3,
3941 network-uri >= 2.6 && < 2.7,
4042 reflex >= 0.5 && < 0.6,
@@ -58,4 +60,5 @@ executable example
5860 default-language: Haskell2010
5961 main-is: Example.hs
6062 other-modules: API
63+ TupleExample
6164 hs-source-dirs: exec
src/Servant/Common/BaseUrl.hs+3−2
@@ -20,18 +20,19 @@ module Servant.Common.BaseUrl (
2020) where
2121
2222import Control.Monad (join)
23−import Control.Monad.IO.Class (MonadIO)
23+-- import Control.Monad.IO.Class (MonadIO)
2424import Control.Monad.Fix (MonadFix)
2525import Data.Monoid ((<>))
2626import Data.Text (Text)
2727import qualified Data.Text as T
2828import GHC.Generics
29+import Language.Javascript.JSaddle.Monad (MonadJSM)
2930import Reflex
3031import Reflex.Dom
3132import Text.Read
3233
3334
34−type SupportsServantReflex t m = (Reflex t, TriggerEvent t m, PerformEvent t m, HasWebView (Performable m), MonadIO (Performable m))
35+type SupportsServantReflex t m = (Reflex t, TriggerEvent t m, PerformEvent t m, HasWebView (Performable m), MonadJSM (Performable m))
3536
3637-- | URI scheme to use
3738data Scheme =
src/Servant/Common/Req.hs+84−36
@@ -25,8 +25,9 @@ import Data.Text (Text)
2525import qualified Data.Text as T
2626import qualified Data.Text.Encoding as TE
2727import Data.Traversable (forM)
28+import Language.Javascript.JSaddle.Monad (JSM, MonadJSM, liftJSM)
2829import Reflex.Dom hiding (tag)
29−import Reflex.Dom.Xhr (newXMLHttpRequest)
30+import Reflex.Dom.Xhr (newXMLHttpRequest)
3031import Servant.Common.BaseUrl (BaseUrl, showBaseUrl,
3132 SupportsServantReflex)
3233import Servant.API.ContentTypes (MimeUnrender(..), NoContent(..))
@@ -139,13 +140,25 @@ data ReqIO = ReqIO
139140defReq :: Req t
140141defReq = Req "GET" [] [] Nothing [] def Nothing
141142
143+defReqIO :: ReqIO
144+defReqIO = ReqIO "GET" [] [] Nothing [] def Nothing
145+
142146prependToPathParts :: Dynamic t (Either Text Text) -> Req t -> Req t
143147prependToPathParts p req =
144148 req { reqPathParts = p : reqPathParts req }
145149
150+
146151addHeader :: (ToHttpApiData a, Reflex t) => Text -> Dynamic t (Either Text a) -> Req t -> Req t
147152addHeader name val req = req { headers = (name, (fmap . fmap) (TE.decodeUtf8 . toHeader) val) : headers req }
148153
154+prependToPathPartsIO :: Text -> ReqIO -> ReqIO
155+prependToPathPartsIO p req =
156+ req { reqPathPartsIO = p : reqPathPartsIO req }
157+
158+addHeaderIO :: (ToHttpApiData a) => Text -> a -> ReqIO -> ReqIO
159+addHeaderIO name val req = req { headersIO = (name, (TE.decodeUtf8 . toHeader) val) : headersIO req }
160+
161+
149162
150163reqToReflexRequest
151164 :: forall t. Reflex t
@@ -362,9 +375,25 @@ performRequests reqMeth rs reqHost trigger = do
362375 resps <- performSomeRequestsAsync reqs
363376 return $ getCompose <$> resps
364377
378+
379+performRequestsIO :: forall t m f tag.(SupportsServantReflex t m, Traversable f)
380+ => Text
381+ -> Event t (f ReqIO)
382+ -> BaseUrl
383+ -> m (Event t (f XhrResponse))
384+performRequestsIO reqMeth rs reqHost = do
385+ -- let xhrReqs = sequence $ (\r -> reqToReflexRequest reqMeth r reqHost) <$> rs :: Dynamic t (f (Either Text (XhrRequest XhrPayload)))
386+ -- let xhrReqs = join $ (\(fxhr :: f (Req t)) -> sequence $ reqToReflexRequest reqMeth reqHost <$> fxhr) <$> rs
387+ let xhrReqs :: (Event t (f (XhrRequest XhrPayload))) = fmap (reqToReflexRequestIO reqMeth reqHost) <$> rs
388+ -- let reqs = attachPromptlyDynWith (\fxhr t -> fxhr) xhrReqs trigger
389+ resps <- performRequestsAsync xhrReqs
390+ return $ resps
391+
392+
365393-- | Issues a collection of requests when the supplied Event fires. When ALL requests from a given firing complete, the results are collected and returned via the return Event.
366394performSomeRequestsAsync
367− :: (MonadIO (Performable m),
395+ :: (MonadJSM (Performable m),
396+ MonadJSM (Performable m),
368397 HasWebView (Performable m),
369398 PerformEvent t m,
370399 TriggerEvent t m,
@@ -377,10 +406,10 @@ performSomeRequestsAsync = performSomeRequestsAsync' newXMLHttpRequest . fmap re
377406
378407------------------------------------------------------------------------------
379408-- | A modified version or Reflex.Dom.Xhr.performRequestsAsync
380−-- that accepts 'f (Either e (XhrRequestb))' events
409+-- that accepts 'f (Either e (XhrRequest))' events
381410performSomeRequestsAsync'
382− :: (MonadIO (Performable m), PerformEvent t m, TriggerEvent t m, Traversable f)
383− => (XhrRequest b -> (a -> IO ()) -> Performable m XMLHttpRequest)
411+ :: (MonadJSM (Performable m), PerformEvent t m, TriggerEvent t m, Traversable f)
412+ => (XhrRequest b -> (a -> JSM ()) -> Performable m XMLHttpRequest)
384413 -> Event t (Performable m (f (Either Text (XhrRequest b)))) -> m (Event t (f (Either Text a)))
385414performSomeRequestsAsync' newXhr req = performEventAsync $ ffor req $ \hrs cb -> do
386415 rs <- hrs
@@ -415,30 +444,30 @@ performRequest reqMeth req reqHost trigger = do
415444 return (resps, badReqs)
416445
417446
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
447+-- -- | This function actually performs the request.
448+-- performRequestIO :: forall t m tag. (SupportsServantReflex t m)
449+-- => Text
450+-- -> ReqIO
451+-- -> BaseUrl
452+-- -> tag
453+-- -> m (Either (tag, XhrResponse) (tag, Text))
454+-- performRequestIO reqMeth req reqHost trigger = do
426455
427− let xhrReq = reqToReflexRequestIO reqMeth reqHost req
428− let reqs = (trigger ,xhrReq )
456+-- let xhrReq = reqToReflexRequestIO reqMeth reqHost req
457+-- let reqs = (trigger ,xhrReq )
429458
430− -- resps <- performRequestsAsync okReqs
459+-- -- resps <- performRequestsAsync okReqs
431460
432− -- let x :: _ = newXMLHttpRequestWithError
461+-- -- let x :: _ = newXMLHttpRequestWithError
433462
434− -- return (resps, badReqs)
435− undefined
463+-- -- return (resps, badReqs)
464+-- undefined
436465
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
466+-- doThing :: (IsXhrPayload a, MonadJSM m, HasJSContext m) => XhrRequest a -> m XhrResponse
467+-- doThing req = do
468+-- v <- liftIO newEmptyMVar
469+-- r <- newXMLHttpRequest req (liftIO . putMVar v)
470+-- liftIO $ takeMVar v
442471
443472
444473type XhrPayload = T.Text
@@ -510,23 +539,23 @@ performRequestsCTIO
510539 MimeUnrender ct a, Traversable f)
511540 => Proxy ct
512541 -> 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
542+ -> Event t (f ReqIO)
543+ -> BaseUrl
544+ -> m (Event t (f (Either Text a)))
545+performRequestsCTIO ct reqMeth reqs reqHost = do
546+ resps <- performRequestsIO reqMeth reqs reqHost
519547 let decodeResp x = first T.pack .
520548 mimeUnrender ct .
521549 BL.fromStrict .
522550 TE.encodeUtf8 =<< note "No body text"
523551 (_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
552+ return $ fmap (fmap decodeResp) resps
553+ -- return $ fmap
554+ -- (\(t,rs) -> ffor rs $ \r -> case r of
555+ -- Left e -> RequestFailure t e
556+ -- Right g -> evalResponse decodeResp (t,g)
557+ -- )
558+ -- resps
530559
531560
532561performRequestsNoBody
@@ -564,6 +593,25 @@ evalResponse decode (tag, xhr) =
564593 else ResponseFailure tag errMsg xhr
565594 in respPayld
566595
596+-- ------------------------------------------------------------------------------
597+-- evalResponseIO
598+-- :: (XhrResponse -> Either Text a)
599+-- -> (XhrResponse)
600+-- -> ReqResult tag a
601+-- evalResponseIO decode (tag, xhr) =
602+-- let okStatus = _xhrResponse_status xhr < 400
603+-- errMsg = fromMaybe
604+-- ("Empty response with error code " <>
605+-- T.pack (show $ _xhrResponse_status xhr))
606+-- (_xhrResponse_responseText xhr)
607+-- respPayld = if okStatus
608+-- then either
609+-- (\e -> ResponseFailure tag e xhr)
610+-- (\v -> ResponseSuccess tag v xhr)
611+-- (decode xhr)
612+-- else ResponseFailure tag errMsg xhr
613+-- in respPayld
614+
567615
568616
569617
src/Servant/Reflex.hs+53−16
@@ -4,6 +4,7 @@
44{-# LANGUAGE FlexibleContexts #-}
55{-# LANGUAGE FlexibleInstances #-}
66{-# LANGUAGE FunctionalDependencies #-}
7+{-# LANGUAGE GADTs #-}
78{-# LANGUAGE InstanceSigs #-}
89{-# LANGUAGE MultiParamTypeClasses #-}
910{-# LANGUAGE OverloadedStrings #-}
@@ -38,7 +39,7 @@ import qualified Data.Set as Set
3839import Data.Text (Text)
3940import qualified Data.Text as T
4041import qualified Data.Text.Encoding as E
41−import GHC.TypeLits (KnownSymbol, symbolVal)
42+import GHC.TypeLits
4243import Servant.API ((:<|>) (..), (:>), BasicAuth,
4344 BasicAuthData, BuildHeadersTo (..),
4445 Capture, Header, Headers (..),
@@ -60,12 +61,13 @@ import Servant.Common.BaseUrl (BaseUrl (..), Scheme (..),
6061 SupportsServantReflex, baseUrlWidget,
6162 showBaseUrl)
6263import Servant.Common.Req (QParam (..), QueryPart (..), Req,
63− ReqIO, ReqResult (..), addHeader,
64+ ReqIO(..), ReqResult (..), addHeader, addHeaderIO,
6465 authData, defReq, evalResponse,
6566 performRequestsCT,
67+ performRequestsCTIO,
6668 performRequestsNoBody,
6769 performSomeRequestsAsync,
68− prependToPathParts, qParamToQueryPart,
70+ prependToPathParts, prependToPathPartsIO, qParamToQueryPart,
6971 qParams, reqBody, reqFailure,
7072 reqMethod, reqSuccess, reqTag,
7173 respHeaders, response)
@@ -97,6 +99,7 @@ client p q t = clientWithRoute p q t defReq
9799class HasClient t m layout (tag :: *) | m -> t where
98100 type Client t m layout tag :: *
99101 type ClientIO m layout tag :: *
102+ type Inp m layout tag :: k
100103 clientWithRoute
101104 :: Proxy layout
102105 -> Proxy m
@@ -112,8 +115,32 @@ class HasClient t m layout (tag :: *) | m -> t where
112115 -> BaseUrl
113116 -> ClientIO m layout tag
114117
118+data HList :: [*] -> * where
119+ HNil :: HList '[]
120+ HCons :: a -> HList xs -> HList (a ': xs)
121+
122+
123+type family ToTuple xs :: *
124+
125+type instance ToTuple (HList '[]) = ()
126+type instance ToTuple (HList (a ': '[])) = a
127+type instance ToTuple (HList (a ': b ': '[])) = (a,b)
128+type instance ToTuple (HList (a ': b ': c ': '[])) = (a,b,c)
129+type instance ToTuple (HList (a ': b ': c ': d ': '[])) = (a,b,c,d)
130+type instance ToTuple (HList (a ': b ': c ': d ': e ': '[])) = (a,b,c,d,e)
131+type instance ToTuple (HList (a ': b ': c ': d ': e ': f ': '[])) = (a,b,c,d,e,f)
132+
133+type instance ToTuple (a :> ()) = a
134+type instance ToTuple (a :> b :> ()) = (a,b)
135+type instance ToTuple (a :> b :> c :> ()) = (a,b,c)
136+type instance ToTuple (a :> b :> c :> d :> ()) = (a,b,c,d)
137+type instance ToTuple (a :> b :> c :> d :> e :> ()) = (a,b,c,d,e)
138+type instance ToTuple (a :> b :> c :> d :> e :> f :> ()) = (a,b,c,d,e,f)
139+
140+
115141instance (HasClient t m a tag, HasClient t m b tag) => HasClient t m (a :<|> b) tag where
116142 type Client t m (a :<|> b) tag = Client t m a tag :<|> Client t m b tag
143+ type Inp m (a :<|> b) tag = Inp m a tag :<|> Inp m b tag
117144 type ClientIO m (a :<|> b) tag = ClientIO m a tag :<|> ClientIO m b tag
118145
119146 clientWithRoute Proxy q pTag req baseurl =
@@ -143,8 +170,11 @@ instance (SupportsServantReflex t m, ToHttpApiData a, HasClient t m sublayout ta
143170 type Client t m (Capture capture a :> sublayout) tag =
144171 Dynamic t (Either Text a) -> Client t m sublayout tag
145172
146− type ClientIO m (Capture capture a :> sublayout) tag =
147− a -> ClientIO m sublayout tag
173+ -- type ClientIO m (Capture capture a :> sublayout) tag =
174+ -- ToTuple (Inp m (Capture capture a :> sublayout) -> ClientIO m sublayout tag
175+
176+ -- type Inp m (Capture capture a :> sublayout) tag =
177+ -- a :> Inp m sublayout tag
148178
149179 clientWithRoute Proxy q t req baseurl val =
150180 clientWithRoute (Proxy :: Proxy sublayout)
@@ -153,12 +183,13 @@ instance (SupportsServantReflex t m, ToHttpApiData a, HasClient t m sublayout ta
153183 baseurl
154184 where p = (fmap . fmap) toUrlPiece val
155185
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
186+ -- clientWithRouteIO Proxy q t req baseurl val =
187+ -- clientWithRouteIO (Proxy :: Proxy sublayout)
188+ -- q t
189+ -- req
190+ -- -- (prependToPathPartsIO p req)
191+ -- baseurl
192+ -- where p = toUrlPiece val
162193
163194
164195-- VERB (Returning content) --
@@ -181,11 +212,10 @@ instance {-# OVERLAPPABLE #-}
181212 where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
182213 req' = req { reqMethod = method }
183214
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
187− where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
188− req' = req { reqMethod = method }
215+ -- clientWithRouteIO Proxy _ _ req baseurl tag =
216+ -- performRequestsCTIO (Proxy :: Proxy ct) method (constDyn $ Identity req') baseurl tag
217+ -- where method = E.decodeUtf8 $ reflectMethod (Proxy :: Proxy method)
218+ -- req' = req { reqMethodIO = method }
189219
190220
191221-- -- VERB (No content) --
@@ -286,6 +316,13 @@ instance (KnownSymbol sym, ToHttpApiData a,
286316 baseurl
287317 where hname = T.pack $ symbolVal (Proxy :: Proxy sym)
288318
319+ -- clientWithRouteIO Proxy q t req baseurl eVal =
320+ -- clientWithRouteIO (Proxy :: Proxy sublayout)
321+ -- q t
322+ -- (Servant.Common.Req.addHeaderIO hname eVal req)
323+ -- baseurl
324+ -- where hname = T.pack $ symbolVal (Proxy :: Proxy sym)
325+
289326
290327
291328
src/Servant/Reflex/Tuple.hsnew file+200
@@ -0,0 +1,200 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE CPP #-}
3+{-# LANGUAGE DataKinds #-}
4+{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE FunctionalDependencies #-}
7+{-# LANGUAGE GADTs #-}
8+{-# LANGUAGE InstanceSigs #-}
9+{-# LANGUAGE MultiParamTypeClasses #-}
10+{-# LANGUAGE OverloadedStrings #-}
11+{-# LANGUAGE PolyKinds #-}
12+{-# LANGUAGE ScopedTypeVariables #-}
13+{-# LANGUAGE TupleSections #-}
14+{-# LANGUAGE TypeFamilies #-}
15+{-# LANGUAGE TypeOperators #-}
16+{-# LANGUAGE UndecidableInstances #-}
17+
18+-- #include "overlapping-compat.h"
19+-- | This module provides 'client' which can automatically generate
20+-- querying functions for each endpoint just from the type representing your
21+-- API.
22+module Servant.Reflex.Tuple
23+ ( client
24+ , BuildHeaderKeysTo(..)
25+ , toHeaders
26+ , HasClient(..)
27+ , module Servant.Common.Req
28+ , module Servant.Common.BaseUrl
29+ ) where
30+
31+------------------------------------------------------------------------------
32+import Control.Applicative
33+import Data.CaseInsensitive (mk)
34+import Data.Functor.Identity
35+import qualified Data.Map as Map
36+import Data.Monoid ((<>))
37+import Data.Proxy (Proxy (..))
38+import qualified Data.Set as Set
39+import Data.Text (Text)
40+import qualified Data.Text as T
41+import qualified Data.Text.Encoding as E
42+import GHC.TypeLits (KnownSymbol, symbolVal)
43+import Servant.API ((:<|>) (..), (:>), BasicAuth,
44+ BasicAuthData, BuildHeadersTo (..),
45+ Capture, Header, Headers (..),
46+ HttpVersion, IsSecure, MimeRender (..),
47+ MimeUnrender, NoContent, QueryFlag,
48+ QueryParam, QueryParams, Raw,
49+ ReflectMethod (..), RemoteHost,
50+ ReqBody, ToHttpApiData (..), Vault,
51+ Verb, contentType)
52+
53+import Reflex.Dom (Dynamic, Event, Reflex,
54+ XhrRequest (..), XhrResponse (..),
55+ XhrResponseHeaders (..),
56+ attachPromptlyDynWith, constDyn, ffor,
57+ fmapMaybe, leftmost,
58+ performRequestsAsync)
59+------------------------------------------------------------------------------
60+import Servant.Common.BaseUrl (BaseUrl (..), Scheme (..),
61+ SupportsServantReflex, baseUrlWidget,
62+ showBaseUrl)
63+import Servant.Common.Req (QParam (..), QueryPart (..), Req,
64+ ReqIO, ReqResult (..), addHeaderIO,
65+ authData, defReqIO, evalResponse,
66+ performRequestsCT,
67+ performRequestsNoBody,
68+ performSomeRequestsAsync,
69+ prependToPathPartsIO, qParamToQueryPart,
70+ qParams, reqBody, reqFailure,
71+ reqMethodIO, reqSuccess, reqTag,
72+ respHeadersIO, response)
73+
74+type family ToTuple a :: *
75+
76+type instance ToTuple () = ()
77+type instance ToTuple (a,()) = a
78+type instance ToTuple (a,(b,())) = (a,b)
79+type instance ToTuple (a,(b,(c,()))) = (a,b,c)
80+type instance ToTuple (a,(b,(c,(d,())))) = (a,b,c)
81+type instance ToTuple (a,(b,(c,(d,(e,()))))) = (a,b,c,d,e)
82+
83+client :: (HasClient layout t m, HasInp layout) => Proxy layout -> Proxy m -> BaseUrl -> Client layout t m
84+client = undefined
85+
86+class HasInp layout where
87+ type Inp layout :: *
88+ type Out layout :: *
89+ inpToReq :: Proxy layout -> Inp layout -> ReqIO
90+
91+class HasClient layout (t :: *) (m :: * -> *) where
92+ type Client layout t m :: *
93+
94+instance (HasClient a t m,
95+ HasClient b t m,
96+ HasInp a,
97+ HasInp b
98+ ) => HasClient (a :<|> b) t m where
99+ type Client (a :<|> b) t m = Client a t m :<|> Client b t m
100+
101+instance (ToHttpApiData a, HasInp sublayout) => HasInp (Capture capture a :> sublayout) where
102+ type Inp (Capture capture a :> sublayout) = (a, Inp sublayout)
103+ type Out (Capture capture a :> sublayout) = Out sublayout
104+ inpToReq _ (a, otherInp) = prependToPathPartsIO (toUrlPiece a) $ inpToReq (Proxy :: Proxy sublayout) otherInp
105+
106+instance (HasInp (Capture capture a :> sublayout)) => HasClient (Capture capture a :> sublayout) t m where
107+ type Client (Capture capture a :> sublayout) t m =
108+ Event t (ToTuple (Inp (Capture capture a :> sublayout))) ->
109+ m (Event t (Out (Capture capture a :> sublayout)))
110+
111+
112+
113+instance {-# OVERLAPPABLE #-}
114+ (MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts)
115+ ) => HasInp (Verb method status cts' a) where
116+ type Inp (Verb method status cts' a) = ()
117+ type Out (Verb method status cts' a) = Either Text a
118+ inpToReq _ _ = defReqIO
119+
120+instance {-# OVERLAPPABLE #-}
121+ (MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts)
122+ ) => HasClient (Verb method status cts' a) t m where
123+ type Client (Verb method status cts' a) t m = Event t (ToTuple (Inp (Verb method status cts' a))) ->
124+ m (Event t (Out (Verb method status cts' a)))
125+
126+
127+instance {-# OVERLAPPING #-}
128+ (ReflectMethod method) => HasInp (Verb method status cts NoContent) where
129+ type Inp (Verb method status cts NoContent) = ()
130+ type Out (Verb method status cts NoContent) = Either Text NoContent
131+
132+
133+instance {-# OVERLAPPING #-}
134+ (ReflectMethod method, SupportsServantReflex t m)
135+ => HasClient (Verb method status cts NoContent) t m where
136+ type Client (Verb method status cts NoContent) t m =
137+ Event t (ToTuple (Inp (Verb method status cts NoContent))) ->
138+ m (Event t (Out (Verb method status cts NoContent)))
139+
140+
141+instance {-# OVERLAPPABLE #-}
142+ (MimeUnrender ct a, BuildHeadersTo ls, BuildHeaderKeysTo ls,
143+ ReflectMethod method, cts' ~ (ct ': cts)
144+ ) => HasInp (Verb method status cts' (Headers ls a)) where
145+ type Inp (Verb method status cts' (Headers ls a)) = ()
146+ type Out (Verb method status cts' (Headers ls a)) = Either Text (Headers ls a)
147+ inpToReq _ _ = defReqIO { respHeadersIO = OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy :: Proxy ls)))}
148+
149+
150+instance {-# OVERLAPPABLE #-}
151+ HasInp (Verb method status cts' (Headers ls a))
152+ => HasClient (Verb method status cts' (Headers ls a)) t m where
153+ type Client (Verb method status cts' (Headers ls a)) t m =
154+ Event t (ToTuple (Inp (Verb method status cts' (Headers ls a)))) ->
155+ m (Event t (Out (Verb method status cts' (Headers ls a))))
156+
157+instance {-# OVERLAPPING #-}
158+ (BuildHeadersTo ls,
159+ BuildHeaderKeysTo ls) => HasInp (Verb method status cts' (Headers ls NoContent)) where
160+ type Inp (Verb method status cts' (Headers ls NoContent)) = ()
161+ type Out (Verb method status cts' (Headers ls NoContent)) = Either Text (Headers ls NoContent)
162+ inpToReq _ _ = defReqIO { respHeadersIO = OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy :: Proxy ls)))}
163+
164+instance {-# OVERLAPPING #-}
165+ (BuildHeadersTo ls,
166+ BuildHeaderKeysTo ls,
167+ SupportsServantReflex t m) => HasClient (Verb method status cts' (Headers ls NoContent)) t m where
168+ type Client (Verb method status cts' (Headers ls NoContent)) t m =
169+ Event t (ToTuple (Inp (Verb method status cts' (Headers ls NoContent)))) ->
170+ m (Event t (Out (Verb method status cts' (Headers ls NoContent))))
171+
172+
173+toHeaders :: BuildHeadersTo ls => ReqResult tag a -> ReqResult tag (Headers ls a)
174+toHeaders r =
175+ let toBS = E.encodeUtf8
176+ hdrs = maybe []
177+ (\xhr -> fmap (\(h,v) -> (mk (toBS h), toBS v))
178+ (Map.toList $ _xhrResponse_headers xhr))
179+ (response r)
180+ in ffor r $ \a -> Headers {getResponse = a ,getHeadersHList = buildHeadersTo hdrs}
181+
182+class BuildHeaderKeysTo hs where
183+ buildHeaderKeysTo :: Proxy hs -> [T.Text]
184+
185+instance {-# OVERLAPPABLE #-} BuildHeaderKeysTo '[]
186+ where buildHeaderKeysTo _ = []
187+
188+instance {-# OVERLAPPABLE #-} (BuildHeaderKeysTo xs, KnownSymbol h)
189+ => BuildHeaderKeysTo ((Header h v) ': xs) where
190+ buildHeaderKeysTo _ = T.pack (symbolVal (Proxy :: Proxy h)) : buildHeaderKeysTo (Proxy :: Proxy xs)
191+
192+
193+instance (KnownSymbol sym, ToHttpApiData a, HasInp sublayout) => HasInp (Header sym a :> sublayout) where
194+ type Inp (Header sym a :> sublayout) = (Maybe a, Inp sublayout)
195+ type Out (Header sym a :> sublayout) = Out sublayout
196+ inpToReq _ (h, vs) = (addHeaderIO hname h) (inpToReq (Proxy :: Proxy sublayout) vs)
197+ where hname = T.pack $ symbolVal (Proxy :: Proxy sym)
198+
199+instance (KnownSymbol sym, ToHttpApiData a, HasClient sublayout t m) => HasClient (Header sym a :> sublayout) t m where
200+ type Client (Header sym a :> sublayout) t m = Event t (ToTuple (Inp (Header sym a :> sublayout))) -> m (Event t (Out (Header sym a :> sublayout)))