git.iko.soy/typeable/servant-reflex
on master help sign in
31 files changed+174−1627
since splitsnapshots
unifiedsplit
.gitignore+2 .travis.yml+4−4 ChangeLog.md+8 README.md+1−1 cabal.project−1 default.nix+10−20 exec/API.hs+1−4 exec/Example.hs+18−157 nix.conf−3 nix/nix.conf+2−1 nix/reflex-platform.nix+2−2 nix/servant-reflex.nix−24 nix/servant-snap.nix+4−6 nix/testdriver.nix−3 release.nix−27 servant-reflex.cabal+16−15 src/Servant/Common/BaseUrl.hs+9−6 src/Servant/Common/Req.hs+6−4 src/Servant/Reflex.hs+45−27 src/Servant/Reflex/Examples.hs−60 src/Servant/Reflex/Internal/TupleRequest.hs−473 src/Servant/Reflex/Multi.hs+35−22 src/Servant/Reflex/TupleRequest.hs−568 src/Servant/Reflex/TupleRequestMulti.hs−153 testdriver/default.nix−16 testdriver/shell.nix−1 testserver/Main.hs+8−12 testserver/default.nix−14 testserver/shell.nix−1 testserver/testserver.cabal+2−2 travis.nix+1
.gitignore+2
@@ -7,3 +7,5 @@ log
77*.stats
88out
99/.stack-work/
10+TAGS
11+dist-newstyle
.travis.yml+4−4
@@ -1,17 +1,17 @@
11language: nix
2−nix: 2.1.1
2+nix: 2.3.6
33sudo: require
44
5−install:
5+before_install:
66 - sudo cp -rT $TRAVIS_BUILD_DIR/nix /etc/nix
77
88before_script:
9+ - sudo systemctl restart nix-daemon.service
910 - git config --global user.email "travis-ci@example.com"
1011 - git config --global user.name "Travis-CI"
1112
1213script:
13− - nix-env -iA cachix -f https://cachix.org/api/v1/install
14− - cachix use imalsogreg
1514 - nix-build travis.nix -A build
15+ - nix-build travis.nix -A buildGhc
1616 - nix-build travis.nix -A testserver
1717 - nix-build travis.nix -A testresults
ChangeLog.md+8
@@ -1,3 +1,11 @@
1+0.3.5
2+-----
3+
4+ - Require reflex-dom-core >= 0.6 (for internal handling of case-insensitive headers)
5+ - Require servant >= 0.16
6+ - Eliminate use of deprecated widgets from reflex-dom-core
7+ - Bump the version of reflex-platform used in nix-shell and test suite
8+
190.3.4
210-----
311
README.md+1−1
@@ -7,7 +7,7 @@
77Keeping your frontend in sync with your API server can be difficult - when the
88API changes its input parameters or return type, XHR requests from the
99frontend will fail at runtime. If your API is defined by
10−[servant](haskell-servant.readthedocs.io) combinators, you can use
10+[servant](https://haskell-servant.readthedocs.io) combinators, you can use
1111`servant-reflex` to share the API between the server and frontend.
1212Syncronization between is checked at compile time, and rather than building XHR
1313requests by hand, API endpoints are available behind `reflex`'s FRP semantics.
cabal.projectdeleted−1
@@ -1 +0,0 @@
1−packages: ./servant-reflex.cabal
default.nix+10−20
@@ -1,30 +1,20 @@
1−{ mkDerivation, aeson, base, bytestring, case-insensitive
2−, containers, data-default, exceptions, generic-lens, ghcjs-dom, http-api-data
3−, http-media, jsaddle, mtl, network-uri, reflex, reflex-dom-core
4−, safe, scientific, servant, servant-auth, stdenv
5−, string-conversions, text, transformers
1+{ mkDerivation, base, bytestring, case-insensitive, containers
2+, data-default, exceptions, ghcjs-dom, http-api-data, http-media
3+, jsaddle, mtl, network-uri, reflex, reflex-dom, reflex-dom-core
4+, safe, servant, servant-auth, stdenv, string-conversions, text
5+, transformers
66}:
77mkDerivation {
88 pname = "servant-reflex";
9− version = "0.3.4";
10− src = builtins.filterSource
11− (path: type:
12− baseNameOf path != "result"
13− && baseNameOf path != "nix"
14− && baseNameOf path != "testdriver"
15− && baseNameOf path != "testserver"
16− ) ./.;
17− configureFlags = [ "-fexample" ];
9+ version = "0.3.6";
10+ src = ./.;
1811 isLibrary = true;
1912 isExecutable = true;
2013 libraryHaskellDepends = [
2114 base bytestring case-insensitive containers data-default exceptions
22− generic-lens ghcjs-dom http-api-data http-media jsaddle mtl network-uri reflex
23− reflex-dom-core safe servant servant-auth string-conversions text
24− transformers
25− ];
26− executableHaskellDepends = [
27− aeson base reflex reflex-dom-core scientific servant text
15+ ghcjs-dom http-api-data http-media jsaddle mtl network-uri reflex
16+ reflex-dom reflex-dom-core safe servant servant-auth string-conversions
17+ text transformers
2818 ];
2919 description = "servant API generator for reflex apps";
3020 license = stdenv.lib.licenses.bsd3;
exec/API.hs+1−4
@@ -31,7 +31,6 @@ instance FromJSON Answer where
3131-- | API spec for server, client, and docs
3232type API = "getunit" :> Get '[JSON] ()
3333 :<|> "getint" :> Get '[JSON] Int
34− :<|> "dblint" :> Capture "anint" Int :> Get '[JSON] Int
3534 :<|> "sayhi" :> QueryParam "username" Text
3635 :> QueryParams "greetings" Text
3736 :> QueryFlag "gusto"
@@ -40,9 +39,7 @@ type API = "getunit" :> Get '[JSON] ()
4039 :> Post '[JSON] Double
4140 :<|> "a" :> "b" :> QueryFlag "gusto" :> Get '[JSON] Text
4241 :<|> "qna" :> ReqBody '[JSON] Question
43− :> Header "qnum" Int
44− :> Header "is-favorite" Bool
45− :> Post '[JSON] (Headers '[Header "anum" Int, Header "was-favorite" Bool] Answer)
42+ :> Post '[JSON] Answer
4643 :<|> "secret" :> BasicAuth "realm" () :> Get '[JSON] Int
4744 :<|> Raw
4845
exec/Example.hs+18−157
@@ -5,8 +5,6 @@
55{-# LANGUAGE RankNTypes #-}
66{-# LANGUAGE RecursiveDo #-}
77{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeApplications #-}
9−{-# LANGUAGE TypeOperators #-}
108
119module Main where
1210
@@ -23,157 +21,21 @@ import Servant.API
2321import API
2422import Data.Proxy
2523import Text.Read (readMaybe)
26−import Reflex.Dom.Core hiding (run)
24+import Reflex.Dom.Core hiding (run, mainWidget)
25+import Reflex.Dom (mainWidget)
2726------------------------------------------------------------------------------
2827import Servant.Reflex
2928import Servant.Reflex.Multi
30−import qualified Servant.Reflex.RequestAPI as SR
3129
3230
3331api :: Proxy API
3432api = Proxy
3533
36−type MyAPI =
37− "getunit" :> Get '[JSON] ()
38− :<|> "getint" :> Get '[JSON] Int
39− :<|> "dblint" :> Capture "anint" Int :> Get '[JSON] Int
40− :<|> "sayhi" :> QueryParam "username" Text
41− :> QueryParams "greetings" Text
42− :> QueryFlag "gusto"
43− :> Get '[JSON] Text
44− :<|> "double" :> ReqBody '[JSON] Double
45− :> Post '[JSON] Double
46− :<|> "a" :> "b" :> QueryFlag "gusto" :> Get '[JSON] Text
47− :<|> "qna" :> ReqBody '[JSON] Question
48− :> Header "qnum" Int
49− :> Header "is-favorite" Bool
50− :> Post '[JSON] (Headers '[Header "anum" Int, Header "was-favorite" Bool] Answer)
51− :<|> "secret" :> BasicAuth "realm" () :> Get '[JSON] Int
52−
5334main :: IO ()
5435main = mainWidget $ do
55− divClass "example-tuple" runTuple
5636 divClass "example-base" run
5737 divClass "example-multi" runMulti
5838
59−hiWidget :: (SupportsServantReflex t m,
60− DomBuilder t m,
61− DomBuilderSpace m ~ GhcjsDomSpace,
62− PostBuild t m) => m (Dynamic t (Maybe Text),
63− Dynamic t [Text],
64− Dynamic t Bool,
65− Event t ())
66−hiWidget = do
67− text "Name"
68− el "br" $ return ()
69− inp :: Dynamic t Text <- fmap value $ divClass "name-input" $ (textInput def)
70− let checkedName = fmap (\i -> bool (Just i) Nothing (T.null i)) inp
71− el "br" $ return ()
72−
73− text "Greetings (space-separated)"
74− el "br" $ return ()
75− greetings <- fmap (fmap T.words . value) $
76− divClass "greetings-input" $ (textInput def)
77−
78− el "br" $ return ()
79−
80− gusto <- fmap value $ divClass "gusto-input" $ checkbox False def
81−
82− el "br" $ return ()
83− sayHiClicks :: Event t () <- divClass "hi-button" $ button "Say hi"
84− return (checkedName, greetings, gusto, sayHiClicks)
85−
86−
87−readWidget :: (SupportsServantReflex t m,
88− DomBuilder t m,
89− DomBuilderSpace m ~ GhcjsDomSpace,
90− PostBuild t m,
91− Read a
92− ) => m (Dynamic t (Maybe a))
93−readWidget = fmap (readMaybe . T.unpack) . value <$> textInput def
94−
95−readWidget' :: (SupportsServantReflex t m,
96− DomBuilder t m,
97− DomBuilderSpace m ~ GhcjsDomSpace,
98− PostBuild t m,
99− Read a
100− ) => a -> m (Dynamic t a)
101−readWidget' d = fmap (fromMaybe d . readMaybe . T.unpack) . value <$> textInput def
102−
103−basicAuthWidget :: (SupportsServantReflex t m,
104− DomBuilder t m,
105− DomBuilderSpace m ~ GhcjsDomSpace,
106− PostBuild t m
107− ) => m (Dynamic t BasicAuthData)
108−basicAuthWidget = do
109− un <- fmap value $ text "Username" >> textArea def
110− pw <- fmap value $ text "Unhidden PW" >> textArea def
111− return $ BasicAuthData <$> fmap T.encodeUtf8 un <*> fmap T.encodeUtf8 pw
112−
113−
114−runTuple :: forall t m. (SupportsServantReflex t m,
115− DomBuilder t m,
116− DomBuilderSpace m ~ GhcjsDomSpace,
117− MonadFix m,
118− PostBuild t m,
119− MonadHold t m) => m ()
120−runTuple = do
121− el "h1" (text "Tuple API")
122− url <- baseUrlWidget
123−
124− let (getUnit :<|> getInt :<|> dblInt :<|> sayHi :<|> dbl :<|> ab :<|> question :<|> secret :<|> _) =
125− SR.clientR (Proxy @API) (Proxy @m) (Proxy @t) (Proxy @[]) url
126−
127− u <- divClass "demo-group" $ do
128− unitBtn <- button "get unit"
129− getUnit (fmap (:[]) unitBtn)
130−
131− (giR, diR) <- divClass "demo-group" $ do
132− int <- readWidget
133− getIntBtn <- button "get int"
134− dblIntBtn <- button "double int"
135− gi <- getInt (fmap (:[]) getIntBtn)
136− di <- dblInt (fmap (:[]) (fmapMaybe id $ tagPromptlyDyn int dblIntBtn))
137− return (gi, di)
138−
139− hiResp <- divClass "demo-group" $ do
140− (nm, greets, gusto, hiClicks) <- hiWidget
141− let hiReqs = tagPromptlyDyn ((,,) <$> nm <*> greets <*> gusto) hiClicks
142− sayHi (fmap (:[]) hiReqs)
143−
144− dblResp <- divClass "demo-group" $ do
145− dblInput <- readWidget
146− dblBtn <- button "double double"
147− dbl $ (fmap (:[])) $ fmapMaybe id $ tagPromptlyDyn dblInput dblBtn
148−
149− twoPathResp <- divClass "demo-group" $ do
150− twoPathInp <- text "Two Path" >> checkbox False def
151− ab (fmap (:[]) $ updated (value twoPathInp))
152−
153− qnaR <- divClass "demo-group" $ do
154− q <- fmap Question . value <$> textInput def
155− i <- readWidget' 0
156− f <- readWidget' False
157− question (fmap (:[]) (updated $ (,,) <$> q <*> i <*> f))
158−
159− secretR <- divClass "demo-group" $ do
160− ba <- basicAuthWidget
161− sBtn <- button "Get secret"
162− secret (fmap pure $ tagPromptlyDyn ba sBtn)
163−
164− divClass "demo-group" $ do
165− el "div" $ text "responses"
166− dynText =<< holdDyn "No responses yet"
167− (leftmost [ tshow <$> u , tshow <$> giR
168− , tshow <$> diR
169− , tshow <$> hiResp , tshow <$> dblResp
170− , tshow <$> twoPathResp
171− , T.concat . fmap (either tshow (\x -> tshow (tshow (getHeaders x), tshow (getResponse x)))) <$> qnaR
172− , tshow <$> secretR
173− ])
174−
175− return ()
176−
17739
17840runMulti :: forall t m. (SupportsServantReflex t m,
17941 DomBuilder t m,
@@ -184,7 +46,7 @@ runMulti :: forall t m. (SupportsServantReflex t m,
18446runMulti = do
18547 url <- baseUrlWidget
18648 el "br" blank
187− let (_ :<|> _ :<|> _ :<|> sayHi :<|> dbl :<|> _ :<|> _ :<|> _ :<|> _ ) =
49+ let (_ :<|> _ :<|> sayHi :<|> dbl :<|> _ :<|> _ :<|> _ :<|> _ ) =
18850 clientA api (Proxy :: Proxy m) (Proxy :: Proxy []) (Proxy :: Proxy Int) url
18951
19052 num :: Dynamic t (Either Text Double) <- fmap (note "No read" . readMaybe . T.unpack) . value <$> textInput def
@@ -237,7 +99,7 @@ run = mdo
23799 -- Name the computed API client functions
238100 let tweakRequest = ClientOptions $ \r -> do
239101 return $ r & withCredentials .~ True
240− let (getUnit :<|> getInt :<|> dblInt :<|> sayhi :<|> dbl
102+ let (getUnit :<|> getInt :<|> sayhi :<|> dbl
241103 :<|> multi :<|> qna :<|> secret :<|> doRaw) =
242104 clientWithOpts api (Proxy :: Proxy m) (Proxy :: Proxy Int) url tweakRequest
243105
@@ -297,7 +159,7 @@ run = mdo
297159 holdDyn "(no errors)" (fmapMaybe reqFailure (dblResp))
298160 el "br" (return ())
299161 divClass "double-result" $ el "p" $ dynText =<<
300− holdDyn "No number yet" (fmap tshow $
162+ holdDyn "No number yet" (fmap tShow $
301163 fmapMaybe reqSuccess (dblResp))
302164 return dblBtn
303165
@@ -306,7 +168,7 @@ run = mdo
306168 b <- value <$> checkbox False def
307169 mpGo <- button "Test"
308170 multiResp <- multi b (tag (current reqCount) mpGo)
309− dynText =<< holdDyn "No res yet" (fmap tshow $
171+ dynText =<< holdDyn "No res yet" (fmap tShow $
310172 fmapMaybe reqSuccess $
311173 (multiResp))
312174 return mpGo
@@ -320,15 +182,14 @@ run = mdo
320182 txt <- value <$> textInput def
321183 ev <- fmap (1 <$) $ button "Question"
322184 let dQ = Right . Question <$> traceDyn "will send: " txt
323− -- rr <- qna dQ ev _ _
185+ rr <- qna dQ ev
324186 el "p" $
325− return ()
326− -- dynText =<< holdDyn "No Answer" (unAnswer <$> fmapMaybe reqSuccess rr)
187+ dynText =<< holdDyn "No Answer" (unAnswer <$> fmapMaybe reqSuccess rr)
327188
328189 divClass "demo-group" $ do
329190 un <- fmap value $ text "Username" >> textArea def
330191 pw <- fmap value $ text "Unhidden PW" >> textArea def
331− let ba :: Dynamic t BasicAuthData = BasicAuthData
192+ let ba :: Dynamic t (BasicAuthData ) = BasicAuthData
332193 <$> fmap T.encodeUtf8 un
333194 <*> fmap T.encodeUtf8 pw
334195 b <- button "Get secret"
@@ -339,20 +200,20 @@ run = mdo
339200
340201showXhrResponse :: XhrResponse -> Text
341202showXhrResponse (XhrResponse stat stattxt rbmay rtmay respHeaders) =
342− T.unlines ["stat: " <> tshow stat
343− ,"stattxt: " <> tshow stattxt
203+ T.unlines ["stat: " <> tShow stat
204+ ,"stattxt: " <> tShow stattxt
344205 ,"resp: " <> maybe "" showRB rbmay
345− ,"rtext: " <> tshow rtmay
346− ,"rHeaders: " <> tshow respHeaders]
206+ ,"rtext: " <> tShow rtmay
207+ ,"rHeaders: " <> tShow respHeaders]
347208
348−tshow :: Show a => a -> Text
349−tshow = T.pack . show
209+tShow :: Show a => a -> Text
210+tShow = T.pack . show
350211
351212showRB :: XhrResponseBody -> Text
352−showRB (XhrResponseBody_Default t) = tshow t
353−showRB (XhrResponseBody_Text t) = tshow t
213+showRB (XhrResponseBody_Default t) = tShow t
214+showRB (XhrResponseBody_Text t) = tShow t
354215showRB (XhrResponseBody_Blob t) = "<Blob>"
355−showRB (XhrResponseBody_ArrayBuffer t) = tshow t
216+showRB (XhrResponseBody_ArrayBuffer t) = tShow t
356217
357218note :: e -> Maybe a -> Either e a
358219note e = maybe (Left e) Right
nix.confdeleted−3
@@ -1,3 +0,0 @@
1−substituters = https://cache.nixos.org https://cache.nixos.org https://nixcache.reflex-frp.org
2−trusted-substituters = https://cache.nixos.org https://cache.nixos.org https://nixcache.reflex-frp.org
3−trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= ryantrinkle.com-1:JJiAKaRv9mWgpVAz8dwewnZe0AzzEAzPkagE9SP5NWI=
nix/nix.conf+2−1
@@ -1,2 +1,3 @@
1−substituters = https://cache.nixos.org https://nixcache.reflex-frp.org
1+substituters = https://cache.nixos.org/ https://nixcache.reflex-frp.org/
22trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= ryantrinkle.com-1:JJiAKaRv9mWgpVAz8dwewnZe0AzzEAzPkagE9SP5NWI=
3+trusted-users = travis
nix/reflex-platform.nix+2−2
@@ -1,7 +1,7 @@
11let
2− rev = "2df9a830193f551ff222ac6b0c739c622578ddc0";
2+ rev = "1aba6f367982bd6dd78ec2fda75ab246a62d32c5" ;
33in import (builtins.fetchTarball
44 {
55 url = "https://github.com/reflex-frp/reflex-platform/archive/${rev}.tar.gz";
6− sha256 = "17084xqhixk79av0gnjgwkhdigf9zwkd4axb6qp89dyyffy5s3hi";
6+ sha256 = "137vg6i8yy4lp6rz4wbvvhfcc9d9qf7j1b3fnng3r7sj63k7j664" ;
77 }) {}
nix/servant-reflex.nixdeleted−24
@@ -1,24 +0,0 @@
1−{ pkgs ? import <nixpkgs> {}, reflexPlatform, compiler ? "ghcjs" }:
2−
3−let
4−
5− # generic-lens-src = pkgs.fetchFromGitHub {
6− # owner = "kcsongor";
7− # repo = "generic-lens";
8− # rev = "6787a4e85d09ad9fcfe5baae53fea2cccbae5976";
9− # sha256 = "1bvjwxm4ik2zcx9s57ibi376y255vm5mawx12s1sjcn09mv1vvdh";
10− # };
11−
12− ghcjsPackages = reflexPlatform.${compiler}.override {
13− overrides = self: super: {
14− servant = super.servant;
15− # generic-lens = pkgs.haskell.lib.dontCheck (self.callCabal2nix "generic-lens" generic-lens-src {
16− # inspection-testing = null;
17− # });
18− wai = super.wai;
19− servant-reflex = pkgs.haskell.lib.appendConfigureFlag
20− (self.callPackage ../. {}) "-fExample";
21− };
22− };
23−
24−in ghcjsPackages.servant-reflex
nix/servant-snap.nix+4−6
@@ -1,9 +1,7 @@
1−# Servant-snap is stuck a bit behind atm. But we have a fork that mostly works
2−# except for streaming, which is good enough for this test server. See:
3−# https://github.com/haskell-servant/servant-snap/issues/20
1+# Servant-snap is marked broken in nixpkgs
42{ nixpkgs ? import <nixpkgs> {}}: self: super:
53self.callCabal2nix "servant-snap" (nixpkgs.fetchgit {
6− url = "https://github.com/antislava/servant-snap.git";
7− rev = "fc9658e8f52ebce9e4f304ea0c6705d697d4fa84";
8− sha256 = "0zlipmx1fb73mhpnndwmdmigxxrsdnsnb1157pgsrxpx89l9pjig";
4+ url = "https://github.com/haskell-servant/servant-snap.git";
5+ rev = "af5172a6de5bb2a07eb1bf4c85952075ec6ecdf3";
6+ sha256 = "sha256-fzzQGwG8Ko72OO/kwLMG52eNZSt3F2dxhNgM9gaO4yQ=";
97}) {}
nix/testdriver.nixdeleted−3
@@ -1,3 +0,0 @@
1−{ reflexPlatform }:
2−
3−reflexPlatform.ghc.callPackage ../testdriver/default.nix {}
release.nixdeleted−27
@@ -1,27 +0,0 @@
1−{ pkgs ? import <nixpkgs> {}, compiler ? "ghcjs" }:
2−
3−let
4− reflexPlatform =
5− import ./nix/reflex-platform.nix {
6− fetchFromGitHub = pkgs.fetchFromGitHub;
7− };
8−
9−in rec {
10−
11− servant-reflex = import ./nix/servant-reflex.nix {
12− reflexPlatform = reflexPlatform;
13− compiler = compiler;
14− };
15− testserver = import ./nix/testserver.nix
16− { reflexPlatform = reflexPlatform;
17− servant-reflex = servant-reflex ;
18− };
19− testdriver = import ./nix/testdriver.nix
20− { reflexPlatform = reflexPlatform;
21− };
22− testresults = import ./nix/testresults.nix
23− { inherit reflexPlatform testserver testdriver;
24− inherit (pkgs) curl;
25− phantomjs = pkgs.phantomjs2;
26− };
27− }
servant-reflex.cabal+16−15
@@ -1,5 +1,5 @@
11Name: servant-reflex
2−Version: 0.3.4
2+Version: 0.3.6
33Synopsis: servant API generator for reflex apps
44Description: Generate reflex-compatible client functions from servant API descriptions
55License: BSD3
@@ -17,16 +17,10 @@ Flag Example
1717
1818library
1919 exposed-modules:
20− Servant.Reflex
21− Servant.Reflex.Examples
22− Servant.Reflex.Multi
23− Servant.Reflex.Internal.TupleRequest
24− Servant.Reflex.TupleRequest
25− Servant.Reflex.TupleRequestMulti
26−
27− other-modules:
2820 Servant.Common.BaseUrl
2921 Servant.Common.Req
22+ Servant.Reflex
23+ Servant.Reflex.Multi
3024
3125 hs-source-dirs: src
3226 build-depends:
@@ -35,19 +29,17 @@ library
3529 case-insensitive >= 1.2.0.4 && < 1.3,
3630 containers >= 0.5.6 && < 0.7,
3731 data-default >= 0.5 && < 0.8,
38− generic-lens,
3932 exceptions >= 0.8 && < 0.11,
4033 ghcjs-dom >= 0.2 && < 0.10,
4134 http-api-data >= 0.3.6 && < 0.5,
4235 http-media >= 0.6 && < 0.9,
4336 jsaddle >= 0.8 && < 0.10,
44− lens,
4537 mtl >= 2.2.1 && < 2.3,
4638 network-uri >= 2.6 && < 2.7,
47− reflex >= 0.5 && < 0.7,
48− reflex-dom-core >= 0.4 && < 0.6,
39+ reflex >= 0.5 && < 0.9,
40+ reflex-dom-core >= 0.6 && < 0.8,
4941 safe >= 0.3.9 && < 0.4,
50− servant >= 0.8 && < 0.18,
42+ servant >= 0.16 && < 0.19,
5143 servant-auth >= 0.2.1 && < 0.4,
5244 string-conversions >= 0.4 && < 0.5,
5345 text >= 1.2 && < 1.3,
@@ -62,7 +54,16 @@ executable example
6254 buildable: True
6355 else
6456 buildable: False
65− build-depends: aeson, reflex, servant-reflex, base, scientific, servant, reflex-dom-core, text
57+ build-depends:
58+ aeson,
59+ reflex,
60+ servant-reflex,
61+ base,
62+ scientific,
63+ servant,
64+ reflex-dom,
65+ reflex-dom-core,
66+ text
6667 default-language: Haskell2010
6768 main-is: Example.hs
6869 other-modules: API
src/Servant/Common/BaseUrl.hs+9−6
@@ -1,4 +1,5 @@
11{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE CPP #-}
23{-# LANGUAGE DeriveDataTypeable #-}
34{-# LANGUAGE DeriveGeneric #-}
45{-# LANGUAGE FlexibleContexts #-}
@@ -31,8 +32,11 @@ import Reflex
3132import Reflex.Dom.Core
3233import Text.Read
3334
34−
35+#if !MIN_VERSION_reflex_dom_core(0,7,0)
3536type SupportsServantReflex t m = (Reflex t, TriggerEvent t m, PerformEvent t m, HasWebView (Performable m), MonadJSM (Performable m))
37+#else
38+type SupportsServantReflex t m = (Reflex t, TriggerEvent t m, PerformEvent t m, MonadJSM (Performable m))
39+#endif
3640
3741-- | URI scheme to use
3842data Scheme =
@@ -85,15 +89,14 @@ baseUrlWidget = elClass "div" "base-url" $ do
8589 where pathWidget :: m (Dynamic t BaseUrl)
8690 pathWidget = do
8791 text "Url base path"
88− t <- textInput (def {_textInputConfig_attributes =
89− constDyn ("placeholder" =: "/a/b")})
92+ t <- inputElement def
9093 return $ BasePath <$> value t
9194 fullUrlWidget :: m (Dynamic t BaseUrl)
9295 fullUrlWidget = do
9396 schm <- dropdown Https (constDyn $ Https =: "https" <> Http =: "http") def
94− srv <- textInput def {_textInputConfig_attributes = constDyn $ "placeholder" =: "example.com"}
97+ srv <- inputElement def
9598 text ":"
96− prt <- textInput def { _textInputConfig_attributes = constDyn $ "placeholder" =: "80"}
99+ prt <- inputElement def
97100 port :: Dynamic t Int <- holdDyn 80 (fmapMaybe (readMaybe . T.unpack) $ updated (value prt))
98− path <- textInput def { _textInputConfig_attributes = constDyn $ "placeholder" =: "a/b" }
101+ path <- inputElement def
99102 return $ BaseFullUrl <$> value schm <*> value srv <*> port <*> value path
src/Servant/Common/Req.hs+6−4
@@ -19,6 +19,7 @@ import Control.Monad.IO.Class (MonadIO, liftIO)
1919import Data.Bifunctor (first)
2020import qualified Data.ByteString.Builder as Builder
2121import qualified Data.ByteString.Lazy.Char8 as BL
22+import Data.ByteString (ByteString)
2223import qualified Data.Map as Map
2324import Data.Maybe (catMaybes, fromMaybe)
2425import Data.Functor.Compose
@@ -244,7 +245,7 @@ reqToReflexRequest reqMeth reqHost req =
244245 , _xhrRequestConfig_user = Nothing
245246 , _xhrRequestConfig_password = Nothing
246247 , _xhrRequestConfig_responseType = Just XhrResponseType_ArrayBuffer
247− , _xhrRequestConfig_sendData = ""
248+ , _xhrRequestConfig_sendData = mempty
248249 , _xhrRequestConfig_withCredentials = False
249250 }
250251 Just rBody -> liftA2 mkConfigBody xhrHeaders rBody
@@ -297,7 +298,9 @@ performRequests reqMeth rs reqHost opts trigger = do
297298performSomeRequestsAsync
298299 :: (MonadIO (Performable m),
299300 MonadJSM (Performable m),
301+#if !MIN_VERSION_reflex_dom_core(0,7,0)
300302 HasWebView (Performable m),
303+#endif
301304 PerformEvent t m,
302305 TriggerEvent t m,
303306 Traversable f,
@@ -335,9 +338,9 @@ performSomeRequestsAsync' opts newXhr req = performEventAsync $ ffor req $ \hrs
335338
336339
337340
338−type XhrPayload = T.Text
341+type XhrPayload = ByteString
339342bytesToPayload :: BL.ByteString -> XhrPayload
340−bytesToPayload = TE.decodeUtf8 . BL.toStrict
343+bytesToPayload = BL.toStrict
341344
342345
343346performRequestsCT
@@ -427,4 +430,3 @@ builderToText = TE.decodeUtf8 . BL.toStrict . Builder.toLazyByteString
427430
428431escape :: T.Text -> T.Text
429432escape = T.pack . N.escapeURIString (not . N.isReserved) . T.unpack . TE.decodeUtf8 . BL.toStrict . Builder.toLazyByteString . toEncodedUrlPiece
430−
src/Servant/Reflex.hs+45−27
@@ -39,34 +39,36 @@ module Servant.Reflex
3939
4040------------------------------------------------------------------------------
4141import Control.Applicative
42−import Data.Monoid ((<>))
43−import qualified Data.Set as Set
44−import qualified Data.Text.Encoding as E
45−import Data.CaseInsensitive (mk)
42+import qualified Data.CaseInsensitive as CI
4643import Data.Functor.Identity
47−import Data.Proxy (Proxy (..))
4844import qualified Data.Map as Map
45+import Data.Monoid ((<>))
46+import Data.Proxy (Proxy (..))
47+import qualified Data.Set as Set
4948import Data.Text (Text)
5049import qualified Data.Text as T
50+import qualified Data.Text.Encoding as E
5151import GHC.Exts (Constraint)
5252import GHC.TypeLits (KnownSymbol, symbolVal)
53−import Servant.API ((:<|>)(..),(:>), BasicAuth,
54− BasicAuthData, BuildHeadersTo(..),
55− Capture, contentType, Header,
56− Headers(..), HttpVersion, IsSecure,
57− MimeRender(..), MimeUnrender,
58− NoContent, QueryFlag, QueryParam,
59− QueryParams, Raw, ReflectMethod(..),
53+import Servant.API ((:<|>) (..), (:>), BasicAuth,
54+ BasicAuthData, BuildHeadersTo (..),
55+ Capture, Header, Headers (..),
56+ HttpVersion, IsSecure,
57+ MimeRender (..), MimeUnrender,
58+ NoContent, QueryFlag, QueryParam',
59+ QueryParams, Raw, ReflectMethod (..),
6060 RemoteHost, ReqBody,
61− ToHttpApiData(..), Vault, Verb)
61+ ToHttpApiData (..), Vault, Verb,
62+ contentType)
63+import Servant.API.Description (Summary)
6264import qualified Servant.Auth as Auth
6365
6466import Reflex.Dom.Core (Dynamic, Event, Reflex,
65− XhrRequest(..),
66− XhrResponseHeaders(..),
67− XhrResponse(..), attachPromptlyDynWith, constDyn, ffor, fmapMaybe,
68− leftmost, performRequestsAsync,
69− )
67+ XhrRequest (..), XhrResponse (..),
68+ XhrResponseHeaders (..),
69+ attachPromptlyDynWith, constDyn, ffor,
70+ fmapMaybe, leftmost,
71+ performRequestsAsync)
7072------------------------------------------------------------------------------
7173import Servant.Common.BaseUrl (BaseUrl(..), Scheme(..), baseUrlWidget,
7274 showBaseUrl,
@@ -101,7 +103,7 @@ import Servant.Common.Req (ClientOptions(..),
101103-- >
102104-- > getAllBooks :: Event t l -> m (Event t (l, ReqResult [Book]))
103105-- > postNewBook :: Dynamic t (Maybe Book) -> Event t l
104−-- -> m (Event t (l, ReqResult Book)))
106+-- > -> m (Event t (l, ReqResult Book)))
105107-- > (getAllBooks :<|> postNewBook) = client myApi host
106108-- > where host = constDyn $ BaseUrl Http "localhost" 8080
107109client
@@ -226,22 +228,26 @@ instance {-# OVERLAPPING #-}
226228
227229toHeaders :: BuildHeadersTo ls => ReqResult tag a -> ReqResult tag (Headers ls a)
228230toHeaders r =
229− let toBS = E.encodeUtf8
230− hdrs = maybe []
231− (\xhr -> fmap (\(h,v) -> (mk (toBS h), toBS v))
231+ let hdrs = maybe []
232+ (\xhr -> fmap (\(h,v) -> (CI.map E.encodeUtf8 h, E.encodeUtf8 v))
232233 (Map.toList $ _xhrResponse_headers xhr))
233234 (response r)
234235 in ffor r $ \a -> Headers {getResponse = a ,getHeadersHList = buildHeadersTo hdrs}
235236
237+
236238class BuildHeaderKeysTo hs where
237− buildHeaderKeysTo :: Proxy hs -> [T.Text]
239+ buildHeaderKeysTo :: Proxy hs -> [CI.CI T.Text]
238240
239241instance {-# OVERLAPPABLE #-} BuildHeaderKeysTo '[]
240242 where buildHeaderKeysTo _ = []
241243
242244instance {-# OVERLAPPABLE #-} (BuildHeaderKeysTo xs, KnownSymbol h)
243245 => BuildHeaderKeysTo ((Header h v) ': xs) where
244− buildHeaderKeysTo _ = T.pack (symbolVal (Proxy :: Proxy h)) : buildHeaderKeysTo (Proxy :: Proxy xs)
246+ buildHeaderKeysTo _ =
247+ let
248+ thisKey = CI.mk $ T.pack (symbolVal (Proxy :: Proxy h))
249+ in thisKey : buildHeaderKeysTo (Proxy :: Proxy xs)
250+
245251
246252-- HEADERS Verb (Content) --
247253-- Headers combinator not treated in fully general case,
@@ -323,6 +329,19 @@ instance HasClient t m sublayout tag
323329 clientWithRouteAndResultHandler Proxy =
324330 clientWithRouteAndResultHandler (Proxy :: Proxy sublayout)
325331
332+-- | Using a 'Summary' combinator in your API doesn't affect the client
333+-- functions.
334+instance (HasClient t m sublayout tag, KnownSymbol sym)
335+ => HasClient t m (Summary sym :> sublayout) tag where
336+
337+ type Client t m (Summary sym :> sublayout) tag =
338+ Client t m sublayout tag
339+
340+ clientWithRouteAndResultHandler Proxy =
341+ clientWithRouteAndResultHandler (Proxy :: Proxy sublayout)
342+
343+
344+
326345
327346-- | If you use a 'QueryParam' in one of your endpoints in your API,
328347-- the corresponding querying function will automatically take
@@ -351,9 +370,9 @@ instance HasClient t m sublayout tag
351370-- > -- 'getBooksBy Nothing' for all books
352371-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
353372instance (KnownSymbol sym, ToHttpApiData a, HasClient t m sublayout tag, Reflex t)
354− => HasClient t m (QueryParam sym a :> sublayout) tag where
373+ => HasClient t m (QueryParam' mods sym a :> sublayout) tag where
355374
356− type Client t m (QueryParam sym a :> sublayout) tag =
375+ type Client t m (QueryParam' mods sym a :> sublayout) tag =
357376 Dynamic t (QParam a) -> Client t m sublayout tag
358377
359378 -- if mparam = Nothing, we don't add it to the query string
@@ -603,4 +622,3 @@ type family HasCookieAuth xs :: Constraint where
603622 HasCookieAuth '[] = CookieAuthNotEnabled
604623
605624class CookieAuthNotEnabled
606−
src/Servant/Reflex/Examples.hsdeleted−60
@@ -1,60 +0,0 @@
1−-- |
2−
3−{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE FlexibleContexts #-}
5−{-# LANGUAGE KindSignatures #-}
6−{-# LANGUAGE OverloadedStrings #-}
7−{-# LANGUAGE RankNTypes #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE TypeApplications #-}
10−{-# LANGUAGE TypeOperators #-}
11−
12−
13−module Servant.Reflex.Examples where
14−
15−import Control.Monad.Identity (Identity(..)) -- Temporary
16−import Data.Proxy (Proxy (..))
17−import Reflex (Event, MonadHold, PostBuild,
18− holdDyn, leftmost)
19−import Reflex.Dom.Core (DomBuilder, display)
20−import Reflex.Dom.Widget (button)
21−import Servant.API ((:<|>) (..), (:>) (..))
22−import qualified Servant.API as API
23−import Servant.Common.BaseUrl (SupportsServantReflex)
24−
25−import qualified Servant.Reflex.TupleRequest as TupleRequest
26−import qualified Servant.Reflex.TupleRequestMulti as TupleRequestMulti
27−import qualified Servant.Test.ComprehensiveAPI as Servant
28−
29−
30−type MyAPI =
31−
32− API.Header "hi" Int
33− :> API.Capture "hello" Int
34− :> API.Capture "hello2" Bool
35− :> API.Get '[API.JSON] (API.Headers '[API.Header "hi" Int] Int)
36−
37− :<|> API.Get '[API.JSON] Bool
38−
39−type MyAPI2 = API.Raw
40−
41−
42−m :: forall t m.(SupportsServantReflex t m, MonadHold t m, PostBuild t m, DomBuilder t m) => m ()
43−m = do
44− let (a :<|> b) = TupleRequest.clientR (Proxy @MyAPI) (Proxy :: Proxy m) (Proxy :: Proxy t) undefined
45− btn <- button "Hello"
46− resA <- a ((2, 5, True) <$ btn)
47− resB <- b (() <$ btn)
48− display =<< holdDyn "waiting" (leftmost [show <$> resB ])
49−
50− let rawR = TupleRequest.clientR (Proxy @MyAPI2) (Proxy @m) (Proxy @t) undefined
51−
52− let (aMulti1 :<|> bMulti1) =
53− TupleRequestMulti.clientR
54− (Proxy @MyAPI)
55− (Proxy :: Proxy m)
56− (Proxy :: Proxy t)
57− (Proxy :: Proxy ((,) Int))
58− undefined
59− return ()
60−
src/Servant/Reflex/Internal/TupleRequest.hsdeleted−473
@@ -1,473 +0,0 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE DeriveGeneric #-}
5−{-# LANGUAGE FlexibleContexts #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE GADTs #-}
8−{-# LANGUAGE InstanceSigs #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
10−{-# LANGUAGE OverloadedStrings #-}
11−{-# LANGUAGE PolyKinds #-}
12−{-# LANGUAGE RankNTypes #-}
13−{-# LANGUAGE ScopedTypeVariables #-}
14−{-# LANGUAGE TupleSections #-}
15−{-# LANGUAGE TypeApplications #-}
16−{-# LANGUAGE TypeFamilies #-}
17−{-# LANGUAGE TypeOperators #-}
18−{-# LANGUAGE UndecidableInstances #-}
19−
20−module Servant.Reflex.Internal.TupleRequest where
21−
22−import Control.Arrow (first, second)
23−import Data.CaseInsensitive (mk)
24−import Control.Lens (over,set)
25−import qualified Data.ByteString.Lazy as BL
26−import qualified Data.Default as Default
27−import qualified Data.List as L
28−import qualified Data.Map as M
29−import qualified Data.Set as Set
30−import Data.Generics.Product
31−import Data.Proxy (Proxy(..))
32−import GHC.TypeLits (KnownSymbol, symbolVal)
33−import Data.Maybe (catMaybes)
34−import Data.Text (Text)
35−import qualified Data.Text as T
36−import qualified Data.Text.Encoding as E
37−import GHC.Generics (Generic)
38−import Reflex.Dom.Xhr (XhrRequest (..), XhrRequestConfig (..),
39− XhrResponseHeaders (..),
40− XhrResponseBody(..),
41− XhrResponseType (..), XhrResponse(..))
42−import Servant.API hiding (HList(..))
43−import Servant.API ((:<|>), BasicAuthData, Raw,
44− basicAuthPassword, basicAuthUsername)
45−
46−import Servant.Reflex (BuildHeaderKeysTo (..),
47− toHeaders)
48−import Servant.Common.BaseUrl (SupportsServantReflex, BaseUrl (..), showBaseUrl)
49−import Servant.Common.Req (escape)
50−
51−
52−
53−-- | A class for generating endpoint-level API client functions
54−-- We need two separate type classes because the needs of endpoints
55−-- and top-level clients are incompatible
56−--
57−-- Endpoints need to accumulate a list of their input types from
58−-- the API definition (the @EInputs@ associated type) when building
59−-- up a function from a sequence of @:>@ combinators. But for
60−-- sequences of @:<|>@ combinators, such a listing doesn't make
61−-- sense. So, we use one class for endpoint-level recursion and
62−-- another for collecting endpoints into the top-level API
63−class EndpointR t m api f where
64−
65− type EInputs t m api f :: [*]
66− type EOutput t m api f :: *
67−
68− mkReq :: HList (EInputs t m api f) -> ReqR
69− getResp :: XhrResponse -> EOutput t m api f
70−
71−data HList xs where
72− HNil :: HList '[]
73− HCons :: x -> HList xs -> HList (x ': xs)
74−
75−
76−class ToTuple xs where
77− type Tuple xs :: *
78− toTuple :: xs -> Tuple xs
79− fromTuple :: Tuple xs -> xs
80−
81−data Sealed a
82−data SealedRaw
83−
84−type family Seal a where
85− Seal (a :<|> b) = Seal a :<|> Seal b
86− Seal Raw = SealedRaw
87− Seal a = Sealed a
88−
89−instance ToTuple (HList '[]) where
90− type Tuple (HList '[]) = ()
91− toTuple _ = ()
92− fromTuple _ = HNil
93−
94−instance ToTuple (HList '[a]) where
95− type Tuple (HList '[a]) = a
96− toTuple (HCons a HNil) = a
97− fromTuple a = HCons a HNil
98−
99−instance ToTuple (HList '[a,b]) where
100− type Tuple (HList '[a,b]) = (a,b)
101− toTuple (HCons a (HCons b HNil)) = (a,b)
102− fromTuple (a,b) = HCons a (HCons b HNil)
103−
104−instance ToTuple (HList '[a,b,c]) where
105− type Tuple (HList '[a,b,c]) = (a,b,c)
106− toTuple (HCons a (HCons b (HCons c HNil))) = (a,b,c)
107− fromTuple (a,b,c) = HCons a (HCons b (HCons c HNil))
108−
109−
110−data ReqR = ReqR
111− { reqRMethod :: T.Text
112− , reqRPathParts :: [T.Text]
113− , reqRParams :: [(T.Text, QueryPart)]
114− , reqRBody :: Maybe (BL.ByteString, T.Text)
115− , reqRHeaders :: [(T.Text, T.Text)]
116− , reqRRespHeaders :: XhrResponseHeaders
117− , reqRAuthData :: Maybe BasicAuthData
118− } deriving (Generic)
119−
120−instance Show ReqR where
121− show (ReqR m pp ps b hs hs' a) = concat $ L.intersperse "\n" [
122− "ReqR {"
123− , ("reqRMethod = " <> show m)
124− , ("reqRPathParts = " <> show pp)
125− , ("reqRParams = " <> show ps)
126− , ("reqRBody = " <> show b)
127− , ("reqRHeaders = " <> show hs)
128− , ("reqRRespHeaders = " <> show hs')
129− , ("reqRAuthData = " <> maybe "Nothing" (const "Just <<redacted>>") a)
130− ]
131−
132−emptyReqR :: T.Text -> ReqR
133−emptyReqR method = ReqR method [] [] Nothing [] (OnlyHeaders mempty) Nothing
134−
135−data QueryPart = QueryPartParam (Maybe Text)
136− | QueryPartParams [Text]
137− | QueryPartFlag Bool
138− deriving (Show)
139−
140−mkXhrRequest :: BaseUrl -> ReqR -> XhrRequest T.Text
141−mkXhrRequest reqHost req =
142− let path = T.intercalate "/" $ reqRPathParts req
143−
144− queryPartString :: (Text, QueryPart) -> Maybe Text
145− queryPartString (pName, qp) = case qp of
146−
147− QueryPartParam Nothing -> Nothing
148− QueryPartParam (Just a) -> Just (pName <> "=" <> escape a)
149−
150− QueryPartParams ps -> Just . T.intercalate "&"
151− $ fmap (\p -> pName <> "=" <> escape p)
152− ps
153−
154− QueryPartFlag True -> Just pName
155− QueryPartFlag False -> Nothing
156−
157− (</>) :: Text -> Text -> Text
158− x </> y | ("/" `T.isSuffixOf` x) || ("/" `T.isPrefixOf` y) = x <> y
159− | otherwise = x <> "/" <> y
160−
161− queryString = T.intercalate "&" . catMaybes . map queryPartString $ reqRParams req
162− url = showBaseUrl reqHost </> path <> if T.null queryString then "" else (T.cons '?' queryString)
163−
164− (headers, body) =
165− let headers0 = M.fromList $ reqRHeaders req
166− in case reqRBody req of
167− Nothing -> (headers0, "")
168− Just (rb,ct) -> (M.insert "Content-Type" ct headers0,
169− E.decodeUtf8 $ BL.toStrict rb)
170−
171− xhrReqConfig = Default.def
172− { _xhrRequestConfig_headers = headers
173− , _xhrRequestConfig_user = E.decodeUtf8 . basicAuthUsername <$> reqRAuthData req
174− , _xhrRequestConfig_password = E.decodeUtf8 . basicAuthPassword <$> reqRAuthData req
175− , _xhrRequestConfig_responseType = Just XhrResponseType_ArrayBuffer
176− , _xhrRequestConfig_sendData = body
177− , _xhrRequestConfig_responseHeaders = reqRRespHeaders req
178− , _xhrRequestConfig_withCredentials = False
179− }
180− xhrReq = XhrRequest (reqRMethod req) url xhrReqConfig
181− in xhrReq
182−
183−
184−instance {-# OVERLAPPING #-}
185− ( MimeUnrender ct a
186− , ReflectMethod method
187− , cts' ~ (ct ': cts)
188− , SupportsServantReflex t m
189− -- , Applicative f
190− )
191− => EndpointR t m (Verb method status cts' a) f where
192− type EInputs t m (Verb method status cts' a) f = '[]
193− type EOutput t m (Verb method status cts' a) f = Either String a
194−
195− mkReq _ = emptyReqR . E.decodeUtf8 $ reflectMethod (Proxy @method)
196− getResp resp = case _xhrResponse_response resp of
197− Just (XhrResponseBody_ArrayBuffer x) -> mimeUnrender (Proxy @ct) (BL.fromStrict x)
198−
199−instance {-# OVERLAPPING #-}
200− ( ReflectMethod method
201− , SupportsServantReflex t m
202− -- , Applicative f
203− )
204− => EndpointR t m (Verb method status cts' NoContent) f where
205− type EInputs t m (Verb method status cts' NoContent) f = '[]
206− type EOutput t m (Verb method status cts' NoContent) f = Either String NoContent
207−
208− mkReq _ = emptyReqR . E.decodeUtf8 $ reflectMethod (Proxy @method)
209− getResp resp = Right NoContent -- TODO: Should EOutput be `NoContent` rather than Either... ?
210−
211−
212−instance {-# OVERLAPPING #-}
213− ( MimeUnrender ct a
214− , BuildHeadersTo hs
215− , ReflectMethod method, cts' ~ (ct ': cts)
216− , SupportsServantReflex t m
217− -- , Applicative f
218− , BuildHeaderKeysTo hs
219− )
220− => EndpointR t m (Verb method status cts' (Headers hs a)) f where
221− type EInputs t m (Verb method status cts' (Headers hs a)) f = '[]
222− type EOutput t m (Verb method status cts' (Headers hs a)) f = Either String (Headers hs a)
223−
224− mkReq _ = set (field @"reqRRespHeaders") (OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy @hs)))) $ emptyReqR $ E.decodeUtf8 $ reflectMethod (Proxy @method)
225− getResp resp = case _xhrResponse_response resp of
226− Just (XhrResponseBody_ArrayBuffer x) ->
227− let hs = buildHeadersTo
228− . fmap (first (mk . E.encodeUtf8) . second E.encodeUtf8)
229− . M.toList
230− $ _xhrResponse_headers resp
231− in fmap (flip Headers (hs)) $ mimeUnrender (Proxy @ct) (BL.fromStrict x)
232−
233−
234−instance {-# OVERLAPPING #-}
235− (MimeUnrender ct a
236− , BuildHeadersTo hs
237− , BuildHeaderKeysTo hs
238− , ReflectMethod method
239− , SupportsServantReflex t m
240− -- , Applicative f
241− )
242− => EndpointR t m (Verb method status cts' (Headers hs NoContent)) f where
243− type EInputs t m (Verb method status cts' (Headers hs NoContent)) f = '[]
244− type EOutput t m (Verb method status cts' (Headers hs NoContent)) f = Either String (Headers hs NoContent)
245−
246− mkReq _ = set (field @"reqRRespHeaders") (OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy @hs)))) $ emptyReqR . E.decodeUtf8 $ reflectMethod (Proxy @method)
247− getResp resp = case _xhrResponse_response resp of
248− Just (XhrResponseBody_ArrayBuffer x) ->
249− let hs = buildHeadersTo
250− . fmap (first (mk . E.encodeUtf8) . second E.encodeUtf8)
251− . M.toList
252− $ _xhrResponse_headers resp
253− in Right (Headers NoContent hs)
254−
255−
256−instance
257− ( SupportsServantReflex t m
258− , ToHttpApiData a
259− , EndpointR t m sublayout f
260− -- , Applicative f
261− )
262− => EndpointR t m (Capture capture a :> sublayout) f where
263−
264− type EInputs t m (Capture capture a :> sublayout) f = a ': EInputs t m sublayout f
265− type EOutput t m (Capture capture a :> sublayout) f = EOutput t m sublayout f
266−
267− mkReq (HCons x xs) = (\a -> over (field @"reqRPathParts") (toUrlPiece a:)) x $
268− mkReq @t @m @sublayout @f xs
269−
270− getResp = getResp @t @m @sublayout @f
271−
272−instance
273− ( SupportsServantReflex t m
274− , ToHttpApiData a
275− , EndpointR t m sublayout f
276− , Applicative f
277− )
278− => EndpointR t m (CaptureAll capture a :> sublayout) f where
279−
280− type EInputs t m (CaptureAll capture a :> sublayout) f = [a] ': EInputs t m sublayout f
281− type EOutput t m (CaptureAll capture a :> sublayout) f = EOutput t m sublayout f
282−
283− mkReq (HCons x xs) = (\a -> over (field @"reqRPathParts") (fmap toUrlPiece a <>)) x $
284− mkReq @t @m @sublayout @f xs
285−
286− getResp = getResp @t @m @sublayout @f
287−
288−
289−instance
290− (SupportsServantReflex t m
291− , KnownSymbol sym
292− , EndpointR t m sublayout f
293− -- , Applicative f
294− )
295− => EndpointR t m (sym :> sublayout) f where
296−
297− type EInputs t m (sym :> sublayout) f = EInputs t m sublayout f
298− type EOutput t m (sym :> sublayout) f = EOutput t m sublayout f
299−
300− mkReq r = over (field @"reqRPathParts") (T.pack (symbolVal (Proxy @sym)) :) $ mkReq @t @m @sublayout @f r
301−
302− getResp = getResp @t @m @sublayout @f
303−
304−instance
305− ( SupportsServantReflex t m
306− , ToHttpApiData a
307− , EndpointR t m sublayout f
308− , KnownSymbol sym
309− -- , Applicative f
310− )
311− => EndpointR t m (QueryParam sym a :> sublayout) f where
312−
313− type EInputs t m (QueryParam sym a :> sublayout) f = Maybe a ': EInputs t m sublayout f
314− type EOutput t m (QueryParam sym a :> sublayout) f = EOutput t m sublayout f
315−
316− mkReq (HCons x xs) = over (field @"reqRParams") ((T.pack $ symbolVal (Proxy @sym),
317− QueryPartParam $ fmap (toQueryParam) x) : ) $
318− mkReq @t @m @sublayout @f xs
319−
320−
321− getResp = getResp @t @m @sublayout @f
322−
323−instance
324− ( SupportsServantReflex t m
325− , ToHttpApiData a
326− , EndpointR t m sublayout f
327− , KnownSymbol sym
328− -- , Applicative f
329− )
330− => EndpointR t m (QueryParams sym a :> sublayout) f where
331−
332− type EInputs t m (QueryParams sym a :> sublayout) f = [a] ': EInputs t m sublayout f
333− type EOutput t m (QueryParams sym a :> sublayout) f = EOutput t m sublayout f
334−
335− mkReq (HCons x xs) =
336− let newParams = (T.pack (symbolVal (Proxy @sym)), QueryPartParams (toQueryParam <$> x))
337− in over (field @"reqRParams") (newParams :) $ mkReq @t @m @sublayout @f xs
338−
339− getResp = getResp @t @m @sublayout @f
340−
341−instance
342− ( SupportsServantReflex t m
343− , EndpointR t m sublayout f
344− , KnownSymbol sym
345− -- , Applicative f
346− )
347− => EndpointR t m (QueryFlag sym :> sublayout) f where
348−
349− type EInputs t m (QueryFlag sym :> sublayout) f = Bool ': EInputs t m sublayout f
350− type EOutput t m (QueryFlag sym :> sublayout) f = EOutput t m sublayout f
351−
352− mkReq (HCons x xs) =
353− let newParam = (T.pack (symbolVal (Proxy @sym)), QueryPartFlag x)
354− in over (field @"reqRParams") (newParam :) $ mkReq @t @m @sublayout @f xs
355−
356− getResp = getResp @t @m @sublayout @f
357−
358−instance
359− ( SupportsServantReflex t m
360− , EndpointR t m sublayout f
361− , MimeRender ct a
362− -- , Applicative f
363− )
364− => EndpointR t m (ReqBody (ct ': cts) a :> sublayout) f where
365−
366−
367− type EInputs t m (ReqBody (ct ': cts) a :> sublayout) f = a ': EInputs t m sublayout f
368− type EOutput t m (ReqBody (ct ': cts) a :> sublayout) f = EOutput t m sublayout f
369−
370− mkReq (HCons x xs) =
371− set (field @"reqRBody") (Just (mimeRender (Proxy @ct) x, T.pack (show (contentType (Proxy @ct))))) $ mkReq @t @m @sublayout @f xs
372−
373− getResp = getResp @t @m @sublayout @f
374−
375−
376−instance
377− ( SupportsServantReflex t m
378− , EndpointR t m sublayout f
379− , KnownSymbol sym
380− , ToHttpApiData a
381− -- , Applicative f
382− )
383− => EndpointR t m (Header sym a :> sublayout) f where
384−
385−
386− type EInputs t m (Header sym a :> sublayout) f = a ': EInputs t m sublayout f
387− type EOutput t m (Header sym a :> sublayout) f = EOutput t m sublayout f
388−
389− mkReq (HCons x xs) =
390− over (field @"reqRHeaders")
391− -- ((symbolVal (Proxy @sym), (mimeRender (Proxy @a) x)) :)
392− ((T.pack $ symbolVal (Proxy @sym), E.decodeUtf8 $ toHeader x) :)
393− $ mkReq @t @m @sublayout @f xs
394−
395− getResp = getResp @t @m @sublayout @f
396−
397−#if MIN_VERSION_servant(0,13,0)
398−-- TODO: Untested
399−instance (SupportsServantReflex t m,
400− EndpointR t m sublayout f,
401− KnownSymbol sym,
402− ToHttpApiData a,
403− Applicative f
404− ) => EndpointR t m (Summary desc :> sublayout) f where
405−
406− type EInputs t m (Summary desc :> sublayout) f = EInputs t m sublayout f
407− type EOutput t m (Summary desc :> sublayout) f = EOutput t m sublayout f
408−
409− mkReq = mkReq @t @m @sublayout @f
410−
411− getResp = getResp @t @m @sublayout @f
412−#endif
413−
414−
415−#if MIN_VERSION_servant(0,13,0)
416−-- TODO: Untested
417−instance (SupportsServantReflex t m,
418− EndpointR t m sublayout f,
419− KnownSymbol sym,
420− ToHttpApiData a,
421− Applicative f
422− ) => EndpointR t m (Description desc :> sublayout) f where
423−
424− type EInputs t m (Description desc :> sublayout) f = EInputs t m sublayout f
425− type EOutput t m (Description desc :> sublayout) f = EOutput t m sublayout f
426−
427− mkReq = mkReq @t @m @sublayout @f
428−
429− getResp = getResp @t @m @sublayout @f
430−#endif
431−
432−instance (SupportsServantReflex t m,
433− EndpointR t m sublayout f,
434− KnownSymbol sym,
435− ToHttpApiData a,
436− Applicative f
437− ) => EndpointR t m (IsSecure :> sublayout) f where
438−
439−
440− type EInputs t m (IsSecure :> sublayout) f = EInputs t m sublayout f
441− type EOutput t m (IsSecure :> sublayout) f = EOutput t m sublayout f
442−
443− mkReq = mkReq @t @m @sublayout @f
444−
445− getResp = getResp @t @m @sublayout @f
446−
447−instance (SupportsServantReflex t m,
448− EndpointR t m sublayout f,
449− KnownSymbol sym,
450− ToHttpApiData a,
451− Applicative f
452− ) => EndpointR t m (Vault :> sublayout) f where
453−
454−
455− type EInputs t m (Vault :> sublayout) f = EInputs t m sublayout f
456− type EOutput t m (Vault :> sublayout) f = EOutput t m sublayout f
457−
458− mkReq = mkReq @t @m @sublayout @f
459−
460− getResp = getResp @t @m @sublayout @f
461−
462−instance (SupportsServantReflex t m,
463− EndpointR t m sublayout f,
464− Applicative f
465− ) => EndpointR t m (BasicAuth realm usr :> sublayout) f where
466−
467− type EInputs t m (BasicAuth realm usr :> sublayout) f = BasicAuthData ': EInputs t m sublayout f
468− type EOutput t m (BasicAuth realm usr :> sublayout) f = EOutput t m sublayout f
469−
470− mkReq (HCons x xs) =
471− set (field @"reqRAuthData") (Just x) $ mkReq @t @m @sublayout @f xs
472−
473− getResp = getResp @t @m @sublayout @f
src/Servant/Reflex/Multi.hs+35−22
@@ -37,28 +37,30 @@ module Servant.Reflex.Multi (
3737 ) where
3838
3939------------------------------------------------------------------------------
40−import Control.Applicative (liftA2)
41−import Data.Functor.Compose (Compose (..), getCompose)
42−import Data.Proxy (Proxy (..))
43−import qualified Data.Set as Set
44−import Data.Text (Text)
45−import qualified Data.Text as T
46−import qualified Data.Text.Encoding as E
47−import GHC.TypeLits (KnownSymbol, symbolVal)
48−import Servant.API ((:<|>) (..), (:>), BasicAuth,
49− BasicAuthData, BuildHeadersTo (..),
50− Capture, Header, Headers (..),
51− HttpVersion, IsSecure, MimeRender (..),
52− MimeUnrender, NoContent, QueryFlag,
53− QueryParam, QueryParams, Raw,
54− ReflectMethod (..), RemoteHost,
55− ReqBody, ToHttpApiData (..), Vault,
56− Verb, contentType)
57−
58−import Reflex.Dom.Core (Dynamic, Event, Reflex,
59− XhrRequest (..),
60− XhrResponseHeaders (..),
61− attachPromptlyDynWith, constDyn)
40+import Control.Applicative (liftA2)
41+import Data.Functor.Compose (Compose (..), getCompose)
42+import Data.Proxy (Proxy (..))
43+import qualified Data.Set as Set
44+import Data.Text (Text)
45+import qualified Data.Text as T
46+import qualified Data.Text.Encoding as E
47+import GHC.TypeLits (KnownSymbol, symbolVal)
48+import Servant.API ((:<|>) (..), (:>), BasicAuth,
49+ BasicAuthData, BuildHeadersTo (..),
50+ Capture, Header, Headers (..),
51+ HttpVersion, IsSecure,
52+ MimeRender (..), MimeUnrender,
53+ NoContent, QueryFlag, QueryParam,
54+ QueryParams, Raw, ReflectMethod (..),
55+ RemoteHost, ReqBody,
56+ ToHttpApiData (..), Vault, Verb,
57+ contentType)
58+import Servant.API.Description (Summary)
59+
60+import Reflex.Dom.Core (Dynamic, Event, Reflex,
61+ XhrRequest (..),
62+ XhrResponseHeaders (..),
63+ attachPromptlyDynWith, constDyn)
6264------------------------------------------------------------------------------
6365import Servant.Common.BaseUrl (BaseUrl (..), Scheme (..),
6466 SupportsServantReflex)
@@ -240,6 +242,17 @@ instance HasClientMulti t m sublayout f tag
240242 clientWithRouteMulti (Proxy :: Proxy sublayout) q f tag
241243
242244
245+------------------------------------------------------------------------------
246+instance (HasClientMulti t m sublayout f tag, KnownSymbol sym)
247+ => HasClientMulti t m (Summary sym :> sublayout) f tag where
248+
249+ type ClientMulti t m (Summary sym :> sublayout) f tag =
250+ ClientMulti t m sublayout f tag
251+
252+ clientWithRouteMulti Proxy q f tag =
253+ clientWithRouteMulti (Proxy :: Proxy sublayout) q f tag
254+
255+
243256------------------------------------------------------------------------------
244257instance (KnownSymbol sym,
245258 ToHttpApiData a,
src/Servant/Reflex/TupleRequest.hsdeleted−568
@@ -1,568 +0,0 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE DeriveGeneric #-}
5−{-# LANGUAGE FlexibleContexts #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE GADTs #-}
8−{-# LANGUAGE InstanceSigs #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
10−{-# LANGUAGE OverloadedStrings #-}
11−{-# LANGUAGE PolyKinds #-}
12−{-# LANGUAGE RankNTypes #-}
13−{-# LANGUAGE ScopedTypeVariables #-}
14−{-# LANGUAGE TupleSections #-}
15−{-# LANGUAGE TypeApplications #-}
16−{-# LANGUAGE TypeFamilies #-}
17−{-# LANGUAGE TypeOperators #-}
18−{-# LANGUAGE UndecidableInstances #-}
19−
20−module Servant.Reflex.TupleRequest where
21−
22−import Control.Arrow (first, second)
23−import Control.Lens
24−import qualified Data.ByteString.Lazy as BL
25−import Data.CaseInsensitive (mk)
26−import qualified Data.Default as Default
27−import Data.Generics.Product
28−import qualified Data.List as L
29−import qualified Data.Map as M
30−import Data.Maybe (catMaybes)
31−import Data.Proxy
32−import Data.Semigroup ((<>))
33−import qualified Data.Set as Set
34−import Data.Text (Text)
35−import qualified Data.Text as T
36−import qualified Data.Text.Encoding as E
37−import GHC.Generics
38−import GHC.TypeLits (KnownSymbol, symbolVal)
39−import Language.Javascript.JSaddle (MonadJSM)
40−import Reflex hiding (HList (..))
41−import Servant.API hiding (HList (..))
42−import Servant.Common.BaseUrl
43−import Servant.Common.Req hiding (QueryPart (..))
44−
45−import Reflex.Dom.Xhr hiding (HList (..))
46−
47−import Servant.Reflex (BuildHeaderKeysTo (..),
48− toHeaders)
49−import Servant.Reflex.Internal.TupleRequest
50−
51−{-
52−TODO:
53− - [x] :<|>
54− - [x] Verb content
55− - [x] Verb nocontent
56− - [x] Verb (Headers content)
57− - [x] Verb (Headers nocontent)
58− - [ ] Stream
59− - [X] Capture :> ...
60− - [ ] Capture' :> ...
61− - [x] CaptureAll :> ...
62− - [x] QueryParam :> ...
63− - [x] QueryParams :> ...
64− - [x] QueryFlag :> ...
65− - [x] ReqBody :> ...
66− - [ ] Header' :> ...
67− - [x] Header :> ...
68− - [x] Summary :> ...
69− - [x] Description :> ...
70− - [x] path :> ...
71− - [ ] RemoteHost :> ...
72− - [x] IsSecure :> ...
73− - [x] Vault :> ...
74− - [ ] WithNamedContext :> ...
75− - [ ] AuthenticatedRequest :> ...
76− - [x] BasicAuth :> ...
77− - [ ] EmptyClient :> ...
78− - [ ] EmptyClient
79− - [ ] HttpVersion :> ...
80−-}
81−
82−clientR
83− :: forall t m layout
84− .( HasClientR t m (Seal layout)
85− , Reflex t
86− )
87− => Proxy layout
88− -> Proxy (m :: * -> *)
89− -> Proxy t
90− -> Dynamic t BaseUrl
91− -> ClientR t m (Seal layout)
92−clientR _ mP tP url =
93− clientRWithRoute (Proxy :: Proxy (Seal layout)) mP undefined url undefined
94− -- empty -^ dynOpts -^
95−
96−-- | A class for generating top-level the API client function
97−class HasClientR t (m :: * -> *) api where
98− type ClientR t m api :: *
99− clientRWithRoute
100− :: Proxy api
101− -> Proxy m
102− -> Dynamic t (Req t)
103− -> Dynamic t BaseUrl
104− -> ClientOptions
105− -> ClientR t m api
106−
107−
108−
109−-- -- | A class for generating endpoint-level API client functions
110−-- -- We need two separate type classes because the needs of endpoints
111−-- -- and top-level clients are incompatible
112−-- --
113−-- -- Endpoints need to accumulate a list of their input types from
114−-- -- the API definition (the @EInputs@ associated type) when building
115−-- -- up a function from a sequence of @:>@ combinators. But for
116−-- -- sequences of @:<|>@ combinators, such a listing doesn't make
117−-- -- sense. So, we use one class for endpoint-level recursion and
118−-- -- another for collecting endpoints into the top-level API
119−-- class EndpointR t m api where
120−
121−-- type EInputs t m api :: [*]
122−-- type EOutput t m api :: *
123−
124−-- mkReq :: HList (EInputs t m api) -> ReqR
125−-- getResp :: XhrResponse -> EOutput t m api
126−
127−
128−------------------------------------------------------------------------------
129−-- | :<|> APIs
130−instance {-# OVERLAPPING #-}
131− (HasClientR t m (layoutLeft),
132− HasClientR t m (layoutRight)
133− ) => HasClientR t m (layoutLeft :<|> layoutRight) where
134− type ClientR t m (layoutLeft :<|> layoutRight) =
135− ClientR t m layoutLeft :<|> ClientR t m layoutRight
136− clientRWithRoute _ mP _ url opts =
137− clientRWithRoute (Proxy @layoutLeft) mP undefined url opts
138− :<|>
139− clientRWithRoute (Proxy @layoutRight) mP undefined url opts
140−
141−#if MIN_VERSION_servant (0,13,0)
142−-- TODO: Untested
143−instance HasClientR t m EmptyAPI where
144− type ClientR t m EmptyAPI = EmptyAPI
145− clientRWithRoute _ _ _ _ _ = EmptyAPI
146−#endif
147−
148−
149−
150−-- | Turn a single Endpoint into a client, by turning the input HList
151−-- into a tuple
152−instance {-# OVERLAPPING #-}
153− forall t m layout
154− .( EndpointR t m layout Identity
155− , ToTuple (HList (EInputs t m layout Identity))
156− , Monad m
157− , SupportsServantReflex t m
158− )
159− => HasClientR t (m :: * -> *) (Sealed layout) where
160− type ClientR t m (Sealed layout) =
161− Event t (Tuple (HList (EInputs t m layout Identity)))
162− -> m (Event t (EOutput t m layout Identity))
163− clientRWithRoute _ mP dReq url opts (reqs :: Event t (Tuple (HList (EInputs t m layout Identity)))) = do
164− let
165− reqTuples :: Event t (HList (EInputs t m layout Identity))
166− reqTuples = (fromTuple @(HList (EInputs t m layout Identity))) <$> reqs
167−
168− -- TODO: replace these 2 lines w/ the next 2. The trace is for library debugging
169− reqRs :: Event t (ReqR)
170− reqRs = (mkReq @t @m @layout @Identity) <$> reqTuples
171−
172− reqs' :: Event t (XhrRequest T.Text)
173− reqs' = ffor (attachPromptlyDyn url reqRs) $ \(bUrl, rs) -> mkXhrRequest bUrl $ rs
174− -- reqs' = ffor (attachPromptlyDyn url reqTuples) $ \(bUrl, inputs) ->
175− -- mkXhrRequest bUrl . mkReq @t @m @layout @f <$> inputs
176− resps <- performRequestAsync $ reqs'
177− -- return $ _ resps
178− return ((getResp @t @m @layout @Identity) <$> resps)
179−
180−
181−instance (SupportsServantReflex t m) => HasClientR t m SealedRaw where
182− type ClientR t m SealedRaw = Event t (XhrRequest T.Text) -> m (Event t XhrResponse)
183− clientRWithRoute _ mP dReq url opts reqs = performRequestAsync reqs
184−
185−
186−-- instance {-# OVERLAPPING #-}(MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts), SupportsServantReflex t m
187−-- ) => EndpointR t m (Verb method status cts' a) where
188−-- type EInputs t m (Verb method status cts' a) = '[]
189−-- type EOutput t m (Verb method status cts' a) = Either String a
190−
191−-- mkReq _ = emptyReqR . E.decodeUtf8 $ reflectMethod (Proxy @method)
192−-- getResp resp = case _xhrResponse_response resp of
193−-- Just (XhrResponseBody_ArrayBuffer x) -> mimeUnrender (Proxy @ct) (BL.fromStrict x)
194−
195−-- instance {-# OVERLAPPING #-}(ReflectMethod method, SupportsServantReflex t m
196−-- ) => EndpointR t m (Verb method status cts' NoContent) where
197−-- type EInputs t m (Verb method status cts' NoContent) = '[]
198−-- type EOutput t m (Verb method status cts' NoContent) = Either String NoContent
199−
200−-- mkReq _ = emptyReqR . E.decodeUtf8 $ reflectMethod (Proxy @method)
201−-- getResp resp = Right NoContent -- TODO: Should EOutput be `NoContent` rather than Either... ?
202−
203−
204−-- instance {-# OVERLAPPING #-}
205−-- (MimeUnrender ct a,
206−-- BuildHeadersTo hs,
207−-- ReflectMethod method, cts' ~ (ct ': cts),
208−-- SupportsServantReflex t m,
209−-- Applicative f,
210−-- BuildHeaderKeysTo hs
211−-- ) => EndpointR t m (Verb method status cts' (Headers hs a)) f where
212−-- type EInputs t m (Verb method status cts' (Headers hs a)) f = '[]
213−-- type EOutput t m (Verb method status cts' (Headers hs a)) f = Either String (Headers hs a)
214−
215−-- mkReq _ = set (field @"reqRRespHeaders") (OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy @hs)))) $ emptyReqR $ E.decodeUtf8 $ reflectMethod (Proxy @method)
216−-- getResp resp = case _xhrResponse_response resp of
217−-- Just (XhrResponseBody_ArrayBuffer x) ->
218−-- let hs = buildHeadersTo
219−-- . fmap (first (mk . E.encodeUtf8) . second E.encodeUtf8)
220−-- . M.toList
221−-- $ _xhrResponse_headers resp
222−-- in fmap (flip Headers (hs)) $ mimeUnrender (Proxy @ct) (BL.fromStrict x)
223−
224−
225−-- instance {-# OVERLAPPING #-}(MimeUnrender ct a,
226−-- BuildHeadersTo hs,
227−-- BuildHeaderKeysTo hs,
228−-- ReflectMethod method,
229−-- SupportsServantReflex t m,
230−-- Applicative f
231−-- ) => EndpointR t m (Verb method status cts' (Headers hs NoContent)) f where
232−-- type EInputs t m (Verb method status cts' (Headers hs NoContent)) f = '[]
233−-- type EOutput t m (Verb method status cts' (Headers hs NoContent)) f = Either String (Headers hs NoContent)
234−
235−-- mkReq _ = set (field @"reqRRespHeaders") (OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy @hs)))) $ emptyReqR . E.decodeUtf8 $ reflectMethod (Proxy @method)
236−-- getResp resp = case _xhrResponse_response resp of
237−-- Just (XhrResponseBody_ArrayBuffer x) ->
238−-- let hs = buildHeadersTo
239−-- . fmap (first (mk . E.encodeUtf8) . second E.encodeUtf8)
240−-- . M.toList
241−-- $ _xhrResponse_headers resp
242−-- in Right (Headers NoContent hs)
243−
244−
245−-- instance (SupportsServantReflex t m,
246−-- ToHttpApiData a,
247−-- EndpointR t m sublayout f,
248−-- Applicative f
249−-- ) => EndpointR t m (Capture capture a :> sublayout) f where
250−
251−-- type EInputs t m (Capture capture a :> sublayout) f = a ': EInputs t m sublayout f
252−-- type EOutput t m (Capture capture a :> sublayout) f = EOutput t m sublayout f
253−
254−-- mkReq (HCons x xs) = (\a -> over (field @"reqRPathParts") (toUrlPiece a:)) x $
255−-- mkReq @t @m @sublayout @f xs
256−
257−-- getResp = getResp @t @m @sublayout @f
258−
259−-- instance (SupportsServantReflex t m,
260−-- ToHttpApiData a,
261−-- EndpointR t m sublayout f,
262−-- Applicative f
263−-- ) => EndpointR t m (CaptureAll capture a :> sublayout) f where
264−
265−-- type EInputs t m (CaptureAll capture a :> sublayout) f = [a] ': EInputs t m sublayout f
266−-- type EOutput t m (CaptureAll capture a :> sublayout) f = EOutput t m sublayout f
267−
268−-- mkReq (HCons x xs) = (\a -> over (field @"reqRPathParts") (fmap toUrlPiece a <>)) x $
269−-- mkReq @t @m @sublayout @f xs
270−
271−-- getResp = getResp @t @m @sublayout @f
272−
273−
274−-- instance (SupportsServantReflex t m,
275−-- KnownSymbol sym,
276−-- EndpointR t m sublayout f,
277−-- Applicative f
278−-- ) => EndpointR t m (sym :> sublayout) f where
279−
280−-- type EInputs t m (sym :> sublayout) f = EInputs t m sublayout f
281−-- type EOutput t m (sym :> sublayout) f = EOutput t m sublayout f
282−
283−-- mkReq r = over (field @"reqRPathParts") (T.pack (symbolVal (Proxy @sym)) :) $ mkReq @t @m @sublayout @f r
284−
285−-- getResp = getResp @t @m @sublayout @f
286−
287−-- instance (SupportsServantReflex t m,
288−-- ToHttpApiData a,
289−-- EndpointR t m sublayout f,
290−-- KnownSymbol sym,
291−-- Applicative f
292−-- ) => EndpointR t m (QueryParam sym a :> sublayout) f where
293−
294−-- type EInputs t m (QueryParam sym a :> sublayout) f = Maybe a ': EInputs t m sublayout f
295−-- type EOutput t m (QueryParam sym a :> sublayout) f = EOutput t m sublayout f
296−
297−-- mkReq (HCons x xs) = over (field @"reqRParams") ((T.pack $ symbolVal (Proxy @sym),
298−-- QueryPartParam $ fmap (toQueryParam) x) : ) $
299−-- mkReq @t @m @sublayout @f xs
300−
301−
302−-- getResp = getResp @t @m @sublayout @f
303−
304−-- instance (SupportsServantReflex t m,
305−-- ToHttpApiData a,
306−-- EndpointR t m sublayout f,
307−-- KnownSymbol sym,
308−-- Applicative f
309−-- ) => EndpointR t m (QueryParams sym a :> sublayout) f where
310−
311−-- type EInputs t m (QueryParams sym a :> sublayout) f = [a] ': EInputs t m sublayout f
312−-- type EOutput t m (QueryParams sym a :> sublayout) f = EOutput t m sublayout f
313−
314−-- mkReq (HCons x xs) =
315−-- let newParams = (T.pack (symbolVal (Proxy @sym)), QueryPartParams (toQueryParam <$> x))
316−-- in over (field @"reqRParams") (newParams :) $ mkReq @t @m @sublayout @f xs
317−
318−-- getResp = getResp @t @m @sublayout @f
319−
320−-- instance (SupportsServantReflex t m,
321−-- EndpointR t m sublayout f,
322−-- KnownSymbol sym,
323−-- Applicative f
324−-- ) => EndpointR t m (QueryFlag sym :> sublayout) f where
325−
326−-- type EInputs t m (QueryFlag sym :> sublayout) f = Bool ': EInputs t m sublayout f
327−-- type EOutput t m (QueryFlag sym :> sublayout) f = EOutput t m sublayout f
328−
329−-- mkReq (HCons x xs) =
330−-- let newParam = (T.pack (symbolVal (Proxy @sym)), QueryPartFlag x)
331−-- in over (field @"reqRParams") (newParam :) $ mkReq @t @m @sublayout @f xs
332−
333−-- getResp = getResp @t @m @sublayout @f
334−
335−-- instance (SupportsServantReflex t m,
336−-- EndpointR t m sublayout f,
337−-- MimeRender ct a,
338−-- Applicative f
339−-- ) => EndpointR t m (ReqBody (ct ': cts) a :> sublayout) f where
340−
341−
342−-- type EInputs t m (ReqBody (ct ': cts) a :> sublayout) f = a ': EInputs t m sublayout f
343−-- type EOutput t m (ReqBody (ct ': cts) a :> sublayout) f = EOutput t m sublayout f
344−
345−-- mkReq (HCons x xs) =
346−-- set (field @"reqRBody") (Just (mimeRender (Proxy @ct) x, T.pack (show (contentType (Proxy @ct))))) $ mkReq @t @m @sublayout @f xs
347−
348−-- getResp = getResp @t @m @sublayout @f
349−
350−
351−-- instance (SupportsServantReflex t m,
352−-- EndpointR t m sublayout f,
353−-- KnownSymbol sym,
354−-- ToHttpApiData a,
355−-- Applicative f
356−-- ) => EndpointR t m (Header sym a :> sublayout) f where
357−
358−
359−-- type EInputs t m (Header sym a :> sublayout) f = a ': EInputs t m sublayout f
360−-- type EOutput t m (Header sym a :> sublayout) f = EOutput t m sublayout f
361−
362−-- mkReq (HCons x xs) =
363−-- over (field @"reqRHeaders")
364−-- -- ((symbolVal (Proxy @sym), (mimeRender (Proxy @a) x)) :)
365−-- ((T.pack $ symbolVal (Proxy @sym), E.decodeUtf8 $ toHeader x) :)
366−-- $ mkReq @t @m @sublayout @f xs
367−
368−-- getResp = getResp @t @m @sublayout @f
369−
370−-- #if MIN_VERSION_servant(0,13,0)
371−-- -- TODO: Untested
372−-- instance (SupportsServantReflex t m,
373−-- EndpointR t m sublayout f,
374−-- KnownSymbol sym,
375−-- ToHttpApiData a,
376−-- Applicative f
377−-- ) => EndpointR t m (Summary desc :> sublayout) f where
378−
379−-- type EInputs t m (Summary desc :> sublayout) f = EInputs t m sublayout f
380−-- type EOutput t m (Summary desc :> sublayout) f = EOutput t m sublayout f
381−
382−-- mkReq = mkReq @t @m @sublayout @f
383−
384−-- getResp = getResp @t @m @sublayout @f
385−-- #endif
386−
387−
388−-- #if MIN_VERSION_servant(0,13,0)
389−-- -- TODO: Untested
390−-- instance (SupportsServantReflex t m,
391−-- EndpointR t m sublayout f,
392−-- KnownSymbol sym,
393−-- ToHttpApiData a,
394−-- Applicative f
395−-- ) => EndpointR t m (Description desc :> sublayout) f where
396−
397−-- type EInputs t m (Description desc :> sublayout) f = EInputs t m sublayout f
398−-- type EOutput t m (Description desc :> sublayout) f = EOutput t m sublayout f
399−
400−-- mkReq = mkReq @t @m @sublayout @f
401−
402−-- getResp = getResp @t @m @sublayout @f
403−-- #endif
404−
405−-- instance (SupportsServantReflex t m,
406−-- EndpointR t m sublayout f,
407−-- KnownSymbol sym,
408−-- ToHttpApiData a,
409−-- Applicative f
410−-- ) => EndpointR t m (IsSecure :> sublayout) f where
411−
412−
413−-- type EInputs t m (IsSecure :> sublayout) f = EInputs t m sublayout f
414−-- type EOutput t m (IsSecure :> sublayout) f = EOutput t m sublayout f
415−
416−-- mkReq = mkReq @t @m @sublayout @f
417−
418−-- getResp = getResp @t @m @sublayout @f
419−
420−-- instance (SupportsServantReflex t m,
421−-- EndpointR t m sublayout f,
422−-- KnownSymbol sym,
423−-- ToHttpApiData a,
424−-- Applicative f
425−-- ) => EndpointR t m (Vault :> sublayout) f where
426−
427−
428−-- type EInputs t m (Vault :> sublayout) f = EInputs t m sublayout f
429−-- type EOutput t m (Vault :> sublayout) f = EOutput t m sublayout f
430−
431−-- mkReq = mkReq @t @m @sublayout @f
432−
433−-- getResp = getResp @t @m @sublayout @f
434−
435−-- instance (SupportsServantReflex t m,
436−-- EndpointR t m sublayout f,
437−-- Applicative f
438−-- ) => EndpointR t m (BasicAuth realm usr :> sublayout) f where
439−
440−-- type EInputs t m (BasicAuth realm usr :> sublayout) f = BasicAuthData ': EInputs t m sublayout f
441−-- type EOutput t m (BasicAuth realm usr :> sublayout) f = EOutput t m sublayout f
442−
443−-- mkReq (HCons x xs) =
444−-- set (field @"reqRAuthData") (Just x) $ mkReq @t @m @sublayout @f xs
445−
446−-- getResp = getResp @t @m @sublayout @f
447−
448−
449−-- instance ToTuple (HList '[]) where
450−-- type Tuple (HList '[]) = ()
451−-- toTuple _ = ()
452−-- fromTuple _ = HNil
453−
454−-- instance ToTuple (HList '[a]) where
455−-- type Tuple (HList '[a]) = a
456−-- toTuple (HCons a HNil) = a
457−-- fromTuple a = HCons a HNil
458−
459−-- instance ToTuple (HList '[a,b]) where
460−-- type Tuple (HList '[a,b]) = (a,b)
461−-- toTuple (HCons a (HCons b HNil)) = (a,b)
462−-- fromTuple (a,b) = HCons a (HCons b HNil)
463−
464−-- instance ToTuple (HList '[a,b,c]) where
465−-- type Tuple (HList '[a,b,c]) = (a,b,c)
466−-- toTuple (HCons a (HCons b (HCons c HNil))) = (a,b,c)
467−-- fromTuple (a,b,c) = HCons a (HCons b (HCons c HNil))
468−
469−-- type MyAPI = Header "hi" Int :> Capture "hello" Int :> Capture "hello2" Bool :> Get '[JSON] (Headers '[Header "hi" Int] Int)
470−-- :<|> Get '[JSON] Bool
471−
472−-- type MyAPI2 = Raw
473−
474−
475−-- m :: forall t m.MonadWidget t m => m ()
476−-- m = do
477−-- let (a :<|> b) = clientR (Proxy @MyAPI) (Proxy :: Proxy m) (Proxy :: Proxy t)
478−-- (Proxy @[]) undefined
479−-- btn <- button "Hello"
480−-- resA <- a (fmap (:[]) ((2, 5,True) <$ btn))
481−-- resB <- b (fmap (:[]) (() <$ btn))
482−-- display =<< holdDyn "waiting" (leftmost [show <$> resB ])
483−-- -- [show <$> resA
484−-- -- , show <$> resB
485−-- -- ])
486−
487−-- let rawR = clientR (Proxy @MyAPI2) (Proxy @m) (Proxy @t) (Proxy @[]) undefined
488−-- return ()
489−
490−-- data ReqR = ReqR
491−-- { reqRMethod :: T.Text
492−-- , reqRPathParts :: [T.Text]
493−-- , reqRParams :: [(T.Text, QueryPart)]
494−-- , reqRBody :: Maybe (BL.ByteString, T.Text)
495−-- , reqRHeaders :: [(T.Text, T.Text)]
496−-- , reqRRespHeaders :: XhrResponseHeaders
497−-- , reqRAuthData :: Maybe BasicAuthData
498−-- } deriving (Generic)
499−
500−-- instance Show ReqR where
501−-- show (ReqR m pp ps b hs hs' a) = concat $ L.intersperse "\n" [
502−-- "ReqR {"
503−-- , ("reqRMethod = " <> show m)
504−-- , ("reqRPathParts = " <> show pp)
505−-- , ("reqRParams = " <> show ps)
506−-- , ("reqRBody = " <> show b)
507−-- , ("reqRHeaders = " <> show hs)
508−-- , ("reqRRespHeaders = " <> show hs')
509−-- , ("reqRAuthData = " <> maybe "Nothing" (const "Just <<redacted>>") a)
510−-- ]
511−
512−-- emptyReqR :: T.Text -> ReqR
513−-- emptyReqR method = ReqR method [] [] Nothing [] (OnlyHeaders mempty) Nothing
514−
515−-- data QueryPart = QueryPartParam (Maybe Text)
516−-- | QueryPartParams [Text]
517−-- | QueryPartFlag Bool
518−-- deriving (Show)
519−
520−-- mkXhrRequest :: BaseUrl -> ReqR -> XhrRequest T.Text
521−-- mkXhrRequest reqHost req =
522−-- let path = T.intercalate "/" $ reqRPathParts req
523−
524−-- queryPartString :: (Text, QueryPart) -> Maybe Text
525−-- queryPartString (pName, qp) = case qp of
526−
527−-- QueryPartParam Nothing -> Nothing
528−-- QueryPartParam (Just a) -> Just (pName <> "=" <> escape a)
529−
530−-- QueryPartParams ps -> Just . T.intercalate "&"
531−-- $ fmap (\p -> pName <> "=" <> escape p)
532−-- ps
533−
534−-- QueryPartFlag True -> Just pName
535−-- QueryPartFlag False -> Nothing
536−
537−-- (</>) :: Text -> Text -> Text
538−-- x </> y | ("/" `T.isSuffixOf` x) || ("/" `T.isPrefixOf` y) = x <> y
539−-- | otherwise = x <> "/" <> y
540−
541−-- queryString = T.intercalate "&" . catMaybes . map queryPartString $ reqRParams req
542−-- url = showBaseUrl reqHost </> path <> if T.null queryString then "" else (T.cons '?' queryString)
543−
544−-- (headers, body) =
545−-- let headers0 = M.fromList $ reqRHeaders req
546−-- in case reqRBody req of
547−-- Nothing -> (headers0, "")
548−-- Just (rb,ct) -> (M.insert "Content-Type" ct headers0,
549−-- E.decodeUtf8 $ BL.toStrict rb)
550−
551−-- xhrReqConfig = def
552−-- { _xhrRequestConfig_headers = headers
553−-- , _xhrRequestConfig_user = E.decodeUtf8 . basicAuthUsername <$> reqRAuthData req
554−-- , _xhrRequestConfig_password = E.decodeUtf8 . basicAuthPassword <$> reqRAuthData req
555−-- , _xhrRequestConfig_responseType = Just XhrResponseType_ArrayBuffer
556−-- , _xhrRequestConfig_sendData = body
557−-- , _xhrRequestConfig_responseHeaders = reqRRespHeaders req
558−-- , _xhrRequestConfig_withCredentials = False
559−-- }
560−-- xhrReq = XhrRequest (reqRMethod req) url xhrReqConfig
561−-- in xhrReq
562−
563−-- transformExample :: MonadWidget t m => ((Int, Char) -> Performable m Bool) -> Event t (Int, Char) -> m (Event t Bool)
564−-- transformExample f = \triggers -> performEvent (f <$> triggers) 1
565−-- -- transformExample = undefined -- let x = performEvent :: _ in undefined
566−
567−-- transform :: MonadWidget t m => forall a. (inp -> IO outp) -> (Event t inp -> m (Event t outp))
568−-- transform = undefined
src/Servant/Reflex/TupleRequestMulti.hsdeleted−153
@@ -1,153 +0,0 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE DeriveGeneric #-}
5−{-# LANGUAGE FlexibleContexts #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE GADTs #-}
8−{-# LANGUAGE InstanceSigs #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
10−{-# LANGUAGE OverloadedStrings #-}
11−{-# LANGUAGE PolyKinds #-}
12−{-# LANGUAGE RankNTypes #-}
13−{-# LANGUAGE ScopedTypeVariables #-}
14−{-# LANGUAGE TupleSections #-}
15−{-# LANGUAGE TypeApplications #-}
16−{-# LANGUAGE TypeFamilies #-}
17−{-# LANGUAGE TypeOperators #-}
18−{-# LANGUAGE UndecidableInstances #-}
19−
20−module Servant.Reflex.TupleRequestMulti where
21−
22−import Control.Arrow (first, second)
23−import Control.Lens
24−import qualified Data.ByteString.Lazy as BL
25−import Data.CaseInsensitive (mk)
26−import qualified Data.Default as Default
27−import Data.Generics.Product
28−import qualified Data.List as L
29−import qualified Data.Map as M
30−import Data.Maybe (catMaybes)
31−import Data.Proxy
32−import Data.Semigroup ((<>))
33−import qualified Data.Set as Set
34−import Data.Text (Text)
35−import qualified Data.Text as T
36−import qualified Data.Text.Encoding as E
37−import GHC.Generics
38−import GHC.TypeLits (KnownSymbol, symbolVal)
39−import Language.Javascript.JSaddle (MonadJSM)
40−import Reflex hiding (HList (..))
41−import Servant.API hiding (HList (..))
42−import Servant.Common.BaseUrl
43−import Servant.Common.Req hiding (QueryPart (..))
44−
45−import Reflex.Dom.Xhr hiding (HList (..))
46−
47−import Servant.Reflex (BuildHeaderKeysTo (..), toHeaders)
48−import Servant.Reflex.Internal.TupleRequest
49−
50−{-
51−TODO:
52− - [x] :<|>
53− - [x] Verb content
54− - [x] Verb nocontent
55− - [x] Verb (Headers content)
56− - [x] Verb (Headers nocontent)
57− - [ ] Stream
58− - [X] Capture :> ...
59− - [ ] Capture' :> ...
60− - [x] CaptureAll :> ...
61− - [x] QueryParam :> ...
62− - [x] QueryParams :> ...
63− - [x] QueryFlag :> ...
64− - [x] ReqBody :> ...
65− - [ ] Header' :> ...
66− - [x] Header :> ...
67− - [x] Summary :> ...
68− - [x] Description :> ...
69− - [x] path :> ...
70− - [ ] RemoteHost :> ...
71− - [x] IsSecure :> ...
72− - [x] Vault :> ...
73− - [ ] WithNamedContext :> ...
74− - [ ] AuthenticatedRequest :> ...
75− - [x] BasicAuth :> ...
76− - [ ] EmptyClient :> ...
77− - [ ] EmptyClient
78− - [ ] HttpVersion :> ...
79−-}
80−
81−clientR
82− :: forall t m layout f
83− . ( HasClientR t m (Seal layout) f
84− -- , Applicative f
85− , Reflex t
86− )
87− => Proxy layout
88− -> Proxy (m :: * -> *)
89− -> Proxy t
90− -> Proxy f
91− -> Dynamic t BaseUrl
92− -> ClientR t m (Seal layout) f
93−clientR _ mP tP fP url =
94− clientRWithRoute (Proxy :: Proxy (Seal layout)) mP fP undefined url undefined
95−
96−-- | A class for generating top-level the API client function
97−class HasClientR t (m :: * -> *) api (f :: * -> *) where
98− type ClientR t m api f :: *
99− clientRWithRoute :: Proxy api -> Proxy m -> Proxy f
100− -> Dynamic t (f (Req t)) -> Dynamic t BaseUrl
101− -> ClientOptions -> ClientR t m api f
102−
103−
104−
105−------------------------------------------------------------------------------
106−-- | :<|> APIs
107−instance {-# OVERLAPPING #-}
108− (HasClientR t m (layoutLeft) f,
109− HasClientR t m (layoutRight) f
110− ) => HasClientR t m (layoutLeft :<|> layoutRight) f where
111− type ClientR t m (layoutLeft :<|> layoutRight) f =
112− ClientR t m layoutLeft f :<|> ClientR t m layoutRight f
113− clientRWithRoute _ tP mP _ url opts =
114− clientRWithRoute (Proxy @layoutLeft) tP mP undefined url opts
115− :<|>
116− clientRWithRoute (Proxy @layoutRight) tP mP undefined url opts
117−
118−#if MIN_VERSION_servant (0,13,0)
119−-- TODO: Untested
120−instance HasClientR t m EmptyAPI f where
121− type ClientR t m EmptyAPI f = EmptyAPI
122− clientRWithRoute _ _ _ _ _ _ = EmptyAPI
123−#endif
124−
125−
126−-- | Turn a single Endpoint into a client, by turning the input HList
127−-- into a tuple
128−instance {-# OVERLAPPING #-} forall t m layout f .(EndpointR t m layout f,
129− ToTuple (HList (EInputs t m layout f)),
130− Monad m
131− , Traversable f
132− , SupportsServantReflex t m
133− ) => HasClientR t (m :: * -> *) (Sealed layout) (f :: * -> *) where
134− type ClientR t m (Sealed layout) f =
135− Event t (f (Tuple (HList (EInputs t m layout f))))
136− -> m (Event t (f (EOutput t m layout f)))
137− clientRWithRoute _ mP fP dReq url opts (reqs :: Event t (f (Tuple (HList (EInputs t m layout f ))))) = do
138− let reqTuples = fmap (fromTuple @(HList (EInputs t m layout f ))) <$> reqs
139−
140− -- TODO: replace these 2 lines w/ the next 2. The trace is for library debugging
141− reqRs = traceEventWith (concat . fmap ((<> "\n") . show)) $ fmap (mkReq @t @m @layout @f) <$> reqTuples
142− reqs' = ffor (attachPromptlyDyn url reqRs) $ \(bUrl, rs) -> mkXhrRequest bUrl <$> rs
143− -- reqs' = ffor (attachPromptlyDyn url reqTuples) $ \(bUrl, inputs) ->
144− -- mkXhrRequest bUrl . mkReq @t @m @layout @f <$> inputs
145− resps <- performRequestsAsync $ reqs'
146− return (fmap (getResp @t @m @layout @f) <$> resps)
147−
148−
149−instance (SupportsServantReflex t m, Traversable f) => HasClientR t m SealedRaw f where
150− type ClientR t m SealedRaw f = Event t (f (XhrRequest T.Text)) -> m (Event t (f XhrResponse))
151− clientRWithRoute _ mP fP dReq url opts reqs = performRequestsAsync reqs
152−
153−
testdriver/default.nixdeleted−16
@@ -1,16 +0,0 @@
1−{ mkDerivation, base, hspec, hspec-webdriver, stdenv, text
2−, webdriver
3−}:
4−mkDerivation {
5− pname = "testdriver";
6− version = "0.2";
7− src = ./.;
8− isLibrary = false;
9− isExecutable = true;
10− executableHaskellDepends = [
11− base hspec hspec-webdriver text webdriver
12− ];
13− description = "Selenium test driver for Servant reflex";
14− license = stdenv.lib.licenses.bsd3;
15− hydraPlatforms = stdenv.lib.platforms.none;
16−}
testdriver/shell.nixdeleted−1
@@ -1 +0,0 @@
1−(import ../release3.nix {}).testdriver.env
testserver/Main.hs+8−12
@@ -20,16 +20,16 @@ import Data.Text hiding (head, length, map,
2020import qualified Data.Text as T
2121import qualified Data.Text.IO as T
2222import GHC.Generics
23−import Snap.Core hiding (addHeader)
23+import Servant.Server.Internal.SnapShims
24+import Snap.Core
2425import Snap.Http.Server
2526
2627import Servant
27−import Servant.Server ()
28−import System.Directory
28+import Servant.Server
2929-- import Snap.Util.FileServe
3030import System.Environment (lookupEnv)
3131import API
32−import Snap hiding (addHeader)
32+import Snap
3333
3434-- * Example
3535
@@ -52,7 +52,7 @@ data App = App { appStaticDir :: String }
5252--
5353-- Each handler runs in the 'ExceptT ServantErr IO' monad.
5454server :: Server API '[BasicAuthCheck (Handler App App) ()] (Handler App App)
55−server = return () :<|> return 100 :<|> dblint :<|> sayhi :<|> dbl
55+server = return () :<|> return 100 :<|> sayhi :<|> dbl
5656 :<|> multi :<|> qna :<|> serveSecret
5757 :<|> statics
5858 where sayhi :: Maybe Text -> [Text] -> Bool -> Handler App App Text
@@ -66,16 +66,15 @@ server = return () :<|> return 100 :<|> dblint :<|> sayhi :<|> dbl
6666 | otherwise = T.intercalate ", " (L.init greetings)
6767 <> ", and " <> L.last greetings <> ", "
6868 return . modifier $ greetPart <> n
69− dblint = return . (* 2)
7069 dbl x = if x `elem` [4,13]
7170 then throwError $ err500 { errBody = "No unlucky numbers please" }
7271 else return $ x * 2
7372 multi = return . bool "Box unchecked" "Box Checked"
74− qna q nm fav = do
73+ qna q = do
7574 liftIO $ do
7675 putStrLn $ "qna got: " ++ show q
7776 T.putStrLn $ unQuestion q
78− return $ addHeader (fromMaybe 10 nm) $ addHeader (fromMaybe False fav) $ (Answer $ unQuestion q)
77+ return $ Answer $ unQuestion q
7978 serveSecret _ = do
8079 req <- getRequest
8180 liftIO $ putStrLn (show req)
@@ -100,7 +99,4 @@ initApp = makeSnaplet "myapp" "example" Nothing $ do
10099
101100-- Put this all to work!
102101main :: IO ()
103−main = commandLineConfig emptyConfig >>= \cfg -> print cfg >> serveSnaplet cfg initApp
104−-- main = serveSnaplet (defaultConfig { accessLog = "/tmp/log/testserver-access.log"
105−-- , errorLog = "/tmp/log/testserver-error.log"
106−-- }) initApp
102+main = serveSnaplet mempty initApp
testserver/default.nixdeleted−14
@@ -1,14 +0,0 @@
1−{ mkDerivation, aeson, base, servant, servant-snap, snap, snap-core
2−, snap-server, stdenv, text
3−}:
4−mkDerivation {
5− pname = "testserver";
6− version = "0.1.0.0";
7− src = ./.;
8− isLibrary = false;
9− isExecutable = true;
10− executableHaskellDepends = [
11− aeson base servant servant-snap snap snap-core snap-server text
12− ];
13− license = stdenv.lib.licenses.bsd3;
14−}
testserver/shell.nixdeleted−1
@@ -1 +0,0 @@
1−(import ../release.nix {}).testserver.env
testserver/testserver.cabal+2−2
@@ -20,12 +20,12 @@ executable back
2020 -- other-modules:
2121 -- other-extensions:
2222 build-depends: aeson >= 0.9 && < 1.5
23− , base >=4.8 && <4.14
23+ , base >=4.8 && < 5
2424 , mtl
2525 , snap >= 1.0 && < 1.2
2626 , snap-server >= 1.0 && < 1.2
2727 , snap-core >= 1.0 && < 1.1
28− , servant >= 0.8 && < 0.18
28+ , servant >= 0.16 && < 0.18
2929 , servant-snap >= 0.8 && < 0.9
3030 , text >= 1.0 && < 1.3
3131 -- hs-source-dirs:
travis.nix+1
@@ -48,6 +48,7 @@ in
4848 inherit reflexPlatform ghcPkgs ghcjsPkgs;
4949
5050 build = ghcjsPkgs.servant-reflex;
51+ buildGhc = ghcPkgs.servant-reflex;
5152 testserver = ghcPkgs.testserver;
5253 testdriver = ghcPkgs.testdriver;
5354 inherit testresults;