on master help sign in
.gitignore+2
since splitsnapshots
unifiedsplit
@@ -7,3 +7,5 @@ log
7*.stats
7*.stats
8out
8out
9/.stack-work/
9/.stack-work/
10+TAGS
11+dist-newstyle
.travis.yml+4−4
@@ -1,17 +1,17 @@
1language: nix
1language: nix
2−nix: 2.1.1
2+nix: 2.3.6
3sudo: require
3sudo: require
4
4
5−install:
5+before_install:
6 - sudo cp -rT $TRAVIS_BUILD_DIR/nix /etc/nix
6 - sudo cp -rT $TRAVIS_BUILD_DIR/nix /etc/nix
7
7
8before_script:
8before_script:
9+ - sudo systemctl restart nix-daemon.service
9 - git config --global user.email "travis-ci@example.com"
10 - git config --global user.email "travis-ci@example.com"
10 - git config --global user.name "Travis-CI"
11 - git config --global user.name "Travis-CI"
11
12
12script:
13script:
13− - nix-env -iA cachix -f https://cachix.org/api/v1/install
14− - cachix use imalsogreg
15 - nix-build travis.nix -A build
14 - nix-build travis.nix -A build
15+ - nix-build travis.nix -A buildGhc
16 - nix-build travis.nix -A testserver
16 - nix-build travis.nix -A testserver
17 - nix-build travis.nix -A testresults
17 - 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+
10.3.4
90.3.4
2-----
10-----
3
11
README.md+1−1
@@ -7,7 +7,7 @@
7Keeping your frontend in sync with your API server can be difficult - when the
7Keeping your frontend in sync with your API server can be difficult - when the
8API changes its input parameters or return type, XHR requests from the
8API changes its input parameters or return type, XHR requests from the
9frontend will fail at runtime. If your API is defined by
9frontend 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
11`servant-reflex` to share the API between the server and frontend.
11`servant-reflex` to share the API between the server and frontend.
12Syncronization between is checked at compile time, and rather than building XHR
12Syncronization between is checked at compile time, and rather than building XHR
13requests by hand, API endpoints are available behind `reflex`'s FRP semantics.
13requests by hand, API endpoints are available behind `reflex`'s FRP semantics.
default.nix+9−17
@@ -1,28 +1,20 @@
1−{ mkDerivation, aeson, base, bytestring, case-insensitive
1+{ mkDerivation, base, bytestring, case-insensitive, containers
2−, containers, data-default, exceptions, ghcjs-dom, http-api-data
2+, data-default, exceptions, ghcjs-dom, http-api-data, http-media
3−, http-media, jsaddle, mtl, network-uri, reflex, reflex-dom-core
3+, jsaddle, mtl, network-uri, reflex, reflex-dom, reflex-dom-core
4−, safe, scientific, servant, servant-auth, stdenv
4+, safe, servant, servant-auth, stdenv, string-conversions, text
5−, string-conversions, text, transformers
5+, transformers
6}:
6}:
7mkDerivation {
7mkDerivation {
8 pname = "servant-reflex";
8 pname = "servant-reflex";
9− version = "0.3.4";
9+ version = "0.3.6";
10− src = builtins.filterSource
11− (path: type:
12− baseNameOf path != "result"
13− && baseNameOf path != "nix"
14− ) ./.;
10+ src = ./.;
15− configureFlags = [ "-fexample" ];
16 isLibrary = true;
11 isLibrary = true;
17 isExecutable = true;
12 isExecutable = true;
18 libraryHaskellDepends = [
13 libraryHaskellDepends = [
19 base bytestring case-insensitive containers data-default exceptions
14 base bytestring case-insensitive containers data-default exceptions
20 ghcjs-dom http-api-data http-media jsaddle mtl network-uri reflex
15 ghcjs-dom http-api-data http-media jsaddle mtl network-uri reflex
21− reflex-dom-core safe servant servant-auth string-conversions text
16+ reflex-dom reflex-dom-core safe servant servant-auth string-conversions
22− transformers
17+ text transformers
23− ];
24− executableHaskellDepends = [
25− aeson base reflex reflex-dom-core scientific servant text
26 ];
18 ];
27 description = "servant API generator for reflex apps";
19 description = "servant API generator for reflex apps";
28 license = stdenv.lib.licenses.bsd3;
20 license = stdenv.lib.licenses.bsd3;
exec/API.hs+1−4
@@ -31,7 +31,6 @@ instance FromJSON Answer where
31-- | API spec for server, client, and docs
31-- | API spec for server, client, and docs
32type API = "getunit" :> Get '[JSON] ()
32type API = "getunit" :> Get '[JSON] ()
33 :<|> "getint" :> Get '[JSON] Int
33 :<|> "getint" :> Get '[JSON] Int
34− :<|> "dblint" :> Capture "anint" Int :> Get '[JSON] Int
35 :<|> "sayhi" :> QueryParam "username" Text
34 :<|> "sayhi" :> QueryParam "username" Text
36 :> QueryParams "greetings" Text
35 :> QueryParams "greetings" Text
37 :> QueryFlag "gusto"
36 :> QueryFlag "gusto"
@@ -40,9 +39,7 @@ type API = "getunit" :> Get '[JSON] ()
40 :> Post '[JSON] Double
39 :> Post '[JSON] Double
41 :<|> "a" :> "b" :> QueryFlag "gusto" :> Get '[JSON] Text
40 :<|> "a" :> "b" :> QueryFlag "gusto" :> Get '[JSON] Text
42 :<|> "qna" :> ReqBody '[JSON] Question
41 :<|> "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
46 :<|> "secret" :> BasicAuth "realm" () :> Get '[JSON] Int
43 :<|> "secret" :> BasicAuth "realm" () :> Get '[JSON] Int
47 :<|> Raw
44 :<|> Raw
48
45
exec/Example.hs+18−157
@@ -5,8 +5,6 @@
5{-# LANGUAGE RankNTypes #-}
5{-# LANGUAGE RankNTypes #-}
6{-# LANGUAGE RecursiveDo #-}
6{-# LANGUAGE RecursiveDo #-}
7{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeApplications #-}
9−{-# LANGUAGE TypeOperators #-}
10
8
11module Main where
9module Main where
12
10
@@ -23,157 +21,21 @@ import Servant.API
23import API
21import API
24import Data.Proxy
22import Data.Proxy
25import Text.Read (readMaybe)
23import Text.Read (readMaybe)
26−import Reflex.Dom.Core hiding (run)
24+import Reflex.Dom.Core hiding (run, mainWidget)
25+import Reflex.Dom (mainWidget)
27------------------------------------------------------------------------------
26------------------------------------------------------------------------------
28import Servant.Reflex
27import Servant.Reflex
29import Servant.Reflex.Multi
28import Servant.Reflex.Multi
30−import qualified Servant.Reflex.RequestAPI as SR
31
29
32
30
33api :: Proxy API
31api :: Proxy API
34api = Proxy
32api = Proxy
35
33
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−
53main :: IO ()
34main :: IO ()
54main = mainWidget $ do
35main = mainWidget $ do
55− divClass "example-tuple" runTuple
56 divClass "example-base" run
36 divClass "example-base" run
57 divClass "example-multi" runMulti
37 divClass "example-multi" runMulti
58
38
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−
177
39
178runMulti :: forall t m. (SupportsServantReflex t m,
40runMulti :: forall t m. (SupportsServantReflex t m,
179 DomBuilder t m,
41 DomBuilder t m,
@@ -184,7 +46,7 @@ runMulti :: forall t m. (SupportsServantReflex t m,
184runMulti = do
46runMulti = do
185 url <- baseUrlWidget
47 url <- baseUrlWidget
186 el "br" blank
48 el "br" blank
187− let (_ :<|> _ :<|> _ :<|> sayHi :<|> dbl :<|> _ :<|> _ :<|> _ :<|> _ ) =
49+ let (_ :<|> _ :<|> sayHi :<|> dbl :<|> _ :<|> _ :<|> _ :<|> _ ) =
188 clientA api (Proxy :: Proxy m) (Proxy :: Proxy []) (Proxy :: Proxy Int) url
50 clientA api (Proxy :: Proxy m) (Proxy :: Proxy []) (Proxy :: Proxy Int) url
189
51
190 num :: Dynamic t (Either Text Double) <- fmap (note "No read" . readMaybe . T.unpack) . value <$> textInput def
52 num :: Dynamic t (Either Text Double) <- fmap (note "No read" . readMaybe . T.unpack) . value <$> textInput def
@@ -237,7 +99,7 @@ run = mdo
237 -- Name the computed API client functions
99 -- Name the computed API client functions
238 let tweakRequest = ClientOptions $ \r -> do
100 let tweakRequest = ClientOptions $ \r -> do
239 return $ r & withCredentials .~ True
101 return $ r & withCredentials .~ True
240− let (getUnit :<|> getInt :<|> dblInt :<|> sayhi :<|> dbl
102+ let (getUnit :<|> getInt :<|> sayhi :<|> dbl
241 :<|> multi :<|> qna :<|> secret :<|> doRaw) =
103 :<|> multi :<|> qna :<|> secret :<|> doRaw) =
242 clientWithOpts api (Proxy :: Proxy m) (Proxy :: Proxy Int) url tweakRequest
104 clientWithOpts api (Proxy :: Proxy m) (Proxy :: Proxy Int) url tweakRequest
243
105
@@ -297,7 +159,7 @@ run = mdo
297 holdDyn "(no errors)" (fmapMaybe reqFailure (dblResp))
159 holdDyn "(no errors)" (fmapMaybe reqFailure (dblResp))
298 el "br" (return ())
160 el "br" (return ())
299 divClass "double-result" $ el "p" $ dynText =<<
161 divClass "double-result" $ el "p" $ dynText =<<
300− holdDyn "No number yet" (fmap tshow $
162+ holdDyn "No number yet" (fmap tShow $
301 fmapMaybe reqSuccess (dblResp))
163 fmapMaybe reqSuccess (dblResp))
302 return dblBtn
164 return dblBtn
303
165
@@ -306,7 +168,7 @@ run = mdo
306 b <- value <$> checkbox False def
168 b <- value <$> checkbox False def
307 mpGo <- button "Test"
169 mpGo <- button "Test"
308 multiResp <- multi b (tag (current reqCount) mpGo)
170 multiResp <- multi b (tag (current reqCount) mpGo)
309− dynText =<< holdDyn "No res yet" (fmap tshow $
171+ dynText =<< holdDyn "No res yet" (fmap tShow $
310 fmapMaybe reqSuccess $
172 fmapMaybe reqSuccess $
311 (multiResp))
173 (multiResp))
312 return mpGo
174 return mpGo
@@ -320,15 +182,14 @@ run = mdo
320 txt <- value <$> textInput def
182 txt <- value <$> textInput def
321 ev <- fmap (1 <$) $ button "Question"
183 ev <- fmap (1 <$) $ button "Question"
322 let dQ = Right . Question <$> traceDyn "will send: " txt
184 let dQ = Right . Question <$> traceDyn "will send: " txt
323− -- rr <- qna dQ ev _ _
185+ rr <- qna dQ ev
324 el "p" $
186 el "p" $
325− return ()
326− -- dynText =<< holdDyn "No Answer" (unAnswer <$> fmapMaybe reqSuccess rr)
187+ dynText =<< holdDyn "No Answer" (unAnswer <$> fmapMaybe reqSuccess rr)
327
188
328 divClass "demo-group" $ do
189 divClass "demo-group" $ do
329 un <- fmap value $ text "Username" >> textArea def
190 un <- fmap value $ text "Username" >> textArea def
330 pw <- fmap value $ text "Unhidden PW" >> textArea def
191 pw <- fmap value $ text "Unhidden PW" >> textArea def
331− let ba :: Dynamic t BasicAuthData = BasicAuthData
192+ let ba :: Dynamic t (BasicAuthData ) = BasicAuthData
332 <$> fmap T.encodeUtf8 un
193 <$> fmap T.encodeUtf8 un
333 <*> fmap T.encodeUtf8 pw
194 <*> fmap T.encodeUtf8 pw
334 b <- button "Get secret"
195 b <- button "Get secret"
@@ -339,20 +200,20 @@ run = mdo
339
200
340showXhrResponse :: XhrResponse -> Text
201showXhrResponse :: XhrResponse -> Text
341showXhrResponse (XhrResponse stat stattxt rbmay rtmay respHeaders) =
202showXhrResponse (XhrResponse stat stattxt rbmay rtmay respHeaders) =
342− T.unlines ["stat: " <> tshow stat
203+ T.unlines ["stat: " <> tShow stat
343− ,"stattxt: " <> tshow stattxt
204+ ,"stattxt: " <> tShow stattxt
344 ,"resp: " <> maybe "" showRB rbmay
205 ,"resp: " <> maybe "" showRB rbmay
345− ,"rtext: " <> tshow rtmay
206+ ,"rtext: " <> tShow rtmay
346− ,"rHeaders: " <> tshow respHeaders]
207+ ,"rHeaders: " <> tShow respHeaders]
347
208
348−tshow :: Show a => a -> Text
209+tShow :: Show a => a -> Text
349−tshow = T.pack . show
210+tShow = T.pack . show
350
211
351showRB :: XhrResponseBody -> Text
212showRB :: XhrResponseBody -> Text
352−showRB (XhrResponseBody_Default t) = tshow t
213+showRB (XhrResponseBody_Default t) = tShow t
353−showRB (XhrResponseBody_Text t) = tshow t
214+showRB (XhrResponseBody_Text t) = tShow t
354showRB (XhrResponseBody_Blob t) = "<Blob>"
215showRB (XhrResponseBody_Blob t) = "<Blob>"
355−showRB (XhrResponseBody_ArrayBuffer t) = tshow t
216+showRB (XhrResponseBody_ArrayBuffer t) = tShow t
356
217
357note :: e -> Maybe a -> Either e a
218note :: e -> Maybe a -> Either e a
358note e = maybe (Left e) Right
219note 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/
2trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= ryantrinkle.com-1:JJiAKaRv9mWgpVAz8dwewnZe0AzzEAzPkagE9SP5NWI=
2trusted-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 @@
1let
1let
2− rev = "a15d3a2411e7ca7d4ee4853b57c72fe83faee272";
2+ rev = "1aba6f367982bd6dd78ec2fda75ab246a62d32c5" ;
3in import (builtins.fetchTarball
3in import (builtins.fetchTarball
4 {
4 {
5 url = "https://github.com/reflex-frp/reflex-platform/archive/${rev}.tar.gz";
5 url = "https://github.com/reflex-frp/reflex-platform/archive/${rev}.tar.gz";
6− sha256 = "1dsvw0lah7761vndip1hqal4fjpjv84ravinnfhy83jgfav5ivna";
6+ sha256 = "137vg6i8yy4lp6rz4wbvvhfcc9d9qf7j1b3fnng3r7sj63k7j664" ;
7 }) {}
7 }) {}
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
1+# Servant-snap is marked broken in nixpkgs
2−# except for streaming, which is good enough for this test server. See:
3−# https://github.com/haskell-servant/servant-snap/issues/20
4{ nixpkgs ? import <nixpkgs> {}}: self: super:
2{ nixpkgs ? import <nixpkgs> {}}: self: super:
5self.callCabal2nix "servant-snap" (nixpkgs.fetchgit {
3self.callCabal2nix "servant-snap" (nixpkgs.fetchgit {
6− url = "https://github.com/antislava/servant-snap.git";
4+ url = "https://github.com/haskell-servant/servant-snap.git";
7− rev = "fc9658e8f52ebce9e4f304ea0c6705d697d4fa84";
5+ rev = "af5172a6de5bb2a07eb1bf4c85952075ec6ecdf3";
8− sha256 = "0zlipmx1fb73mhpnndwmdmigxxrsdnsnb1157pgsrxpx89l9pjig";
6+ sha256 = "sha256-fzzQGwG8Ko72OO/kwLMG52eNZSt3F2dxhNgM9gaO4yQ=";
9}) {}
7}) {}
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−11
@@ -1,5 +1,5 @@
1Name: servant-reflex
1Name: servant-reflex
2−Version: 0.3.4
2+Version: 0.3.6
3Synopsis: servant API generator for reflex apps
3Synopsis: servant API generator for reflex apps
4Description: Generate reflex-compatible client functions from servant API descriptions
4Description: Generate reflex-compatible client functions from servant API descriptions
5License: BSD3
5License: BSD3
@@ -17,12 +17,10 @@ Flag Example
17
17
18library
18library
19 exposed-modules:
19 exposed-modules:
20− Servant.Reflex
21− Servant.Reflex.Multi
22−
23− other-modules:
24 Servant.Common.BaseUrl
20 Servant.Common.BaseUrl
25 Servant.Common.Req
21 Servant.Common.Req
22+ Servant.Reflex
23+ Servant.Reflex.Multi
26
24
27 hs-source-dirs: src
25 hs-source-dirs: src
28 build-depends:
26 build-depends:
@@ -31,19 +29,17 @@ library
31 case-insensitive >= 1.2.0.4 && < 1.3,
29 case-insensitive >= 1.2.0.4 && < 1.3,
32 containers >= 0.5.6 && < 0.7,
30 containers >= 0.5.6 && < 0.7,
33 data-default >= 0.5 && < 0.8,
31 data-default >= 0.5 && < 0.8,
34− generic-lens,
35 exceptions >= 0.8 && < 0.11,
32 exceptions >= 0.8 && < 0.11,
36 ghcjs-dom >= 0.2 && < 0.10,
33 ghcjs-dom >= 0.2 && < 0.10,
37 http-api-data >= 0.3.6 && < 0.5,
34 http-api-data >= 0.3.6 && < 0.5,
38 http-media >= 0.6 && < 0.9,
35 http-media >= 0.6 && < 0.9,
39 jsaddle >= 0.8 && < 0.10,
36 jsaddle >= 0.8 && < 0.10,
40− lens,
41 mtl >= 2.2.1 && < 2.3,
37 mtl >= 2.2.1 && < 2.3,
42 network-uri >= 2.6 && < 2.7,
38 network-uri >= 2.6 && < 2.7,
43− reflex >= 0.5 && < 0.7,
39+ reflex >= 0.5 && < 0.9,
44− reflex-dom-core >= 0.4 && < 0.6,
40+ reflex-dom-core >= 0.6 && < 0.7,
45 safe >= 0.3.9 && < 0.4,
41 safe >= 0.3.9 && < 0.4,
46− servant >= 0.8 && < 0.18,
42+ servant >= 0.16 && < 0.18,
47 servant-auth >= 0.2.1 && < 0.4,
43 servant-auth >= 0.2.1 && < 0.4,
48 string-conversions >= 0.4 && < 0.5,
44 string-conversions >= 0.4 && < 0.5,
49 text >= 1.2 && < 1.3,
45 text >= 1.2 && < 1.3,
@@ -58,7 +54,16 @@ executable example
58 buildable: True
54 buildable: True
59 else
55 else
60 buildable: False
56 buildable: False
61− 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
62 default-language: Haskell2010
67 default-language: Haskell2010
63 main-is: Example.hs
68 main-is: Example.hs
64 other-modules: API
69 other-modules: API
src/Servant/Common/BaseUrl.hs+4−5
@@ -85,15 +85,14 @@ baseUrlWidget = elClass "div" "base-url" $ do
85 where pathWidget :: m (Dynamic t BaseUrl)
85 where pathWidget :: m (Dynamic t BaseUrl)
86 pathWidget = do
86 pathWidget = do
87 text "Url base path"
87 text "Url base path"
88− t <- textInput (def {_textInputConfig_attributes =
88+ t <- inputElement def
89− constDyn ("placeholder" =: "/a/b")})
90 return $ BasePath <$> value t
89 return $ BasePath <$> value t
91 fullUrlWidget :: m (Dynamic t BaseUrl)
90 fullUrlWidget :: m (Dynamic t BaseUrl)
92 fullUrlWidget = do
91 fullUrlWidget = do
93 schm <- dropdown Https (constDyn $ Https =: "https" <> Http =: "http") def
92 schm <- dropdown Https (constDyn $ Https =: "https" <> Http =: "http") def
94− srv <- textInput def {_textInputConfig_attributes = constDyn $ "placeholder" =: "example.com"}
93+ srv <- inputElement def
95 text ":"
94 text ":"
96− prt <- textInput def { _textInputConfig_attributes = constDyn $ "placeholder" =: "80"}
95+ prt <- inputElement def
97 port :: Dynamic t Int <- holdDyn 80 (fmapMaybe (readMaybe . T.unpack) $ updated (value prt))
96 port :: Dynamic t Int <- holdDyn 80 (fmapMaybe (readMaybe . T.unpack) $ updated (value prt))
98− path <- textInput def { _textInputConfig_attributes = constDyn $ "placeholder" =: "a/b" }
97+ path <- inputElement def
99 return $ BaseFullUrl <$> value schm <*> value srv <*> port <*> value path
98 return $ BaseFullUrl <$> value schm <*> value srv <*> port <*> value path
src/Servant/Common/Req.hs+4−3
@@ -19,6 +19,7 @@ import Control.Monad.IO.Class (MonadIO, liftIO)
19import Data.Bifunctor (first)
19import Data.Bifunctor (first)
20import qualified Data.ByteString.Builder as Builder
20import qualified Data.ByteString.Builder as Builder
21import qualified Data.ByteString.Lazy.Char8 as BL
21import qualified Data.ByteString.Lazy.Char8 as BL
22+import Data.ByteString (ByteString)
22import qualified Data.Map as Map
23import qualified Data.Map as Map
23import Data.Maybe (catMaybes, fromMaybe)
24import Data.Maybe (catMaybes, fromMaybe)
24import Data.Functor.Compose
25import Data.Functor.Compose
@@ -244,7 +245,7 @@ reqToReflexRequest reqMeth reqHost req =
244 , _xhrRequestConfig_user = Nothing
245 , _xhrRequestConfig_user = Nothing
245 , _xhrRequestConfig_password = Nothing
246 , _xhrRequestConfig_password = Nothing
246 , _xhrRequestConfig_responseType = Just XhrResponseType_ArrayBuffer
247 , _xhrRequestConfig_responseType = Just XhrResponseType_ArrayBuffer
247− , _xhrRequestConfig_sendData = ""
248+ , _xhrRequestConfig_sendData = mempty
248 , _xhrRequestConfig_withCredentials = False
249 , _xhrRequestConfig_withCredentials = False
249 }
250 }
250 Just rBody -> liftA2 mkConfigBody xhrHeaders rBody
251 Just rBody -> liftA2 mkConfigBody xhrHeaders rBody
@@ -335,9 +336,9 @@ performSomeRequestsAsync' opts newXhr req = performEventAsync $ ffor req $ \hrs
335
336
336
337
337
338
338−type XhrPayload = T.Text
339+type XhrPayload = ByteString
339bytesToPayload :: BL.ByteString -> XhrPayload
340bytesToPayload :: BL.ByteString -> XhrPayload
340−bytesToPayload = TE.decodeUtf8 . BL.toStrict
341+bytesToPayload = BL.toStrict
341
342
342
343
343performRequestsCT
344performRequestsCT
src/Servant/Reflex.hs+42−23
@@ -39,34 +39,36 @@ module Servant.Reflex
39
39
40------------------------------------------------------------------------------
40------------------------------------------------------------------------------
41import Control.Applicative
41import 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
46import Data.Functor.Identity
43import Data.Functor.Identity
47−import Data.Proxy (Proxy (..))
48import qualified Data.Map as Map
44import qualified Data.Map as Map
45+import Data.Monoid ((<>))
46+import Data.Proxy (Proxy (..))
47+import qualified Data.Set as Set
49import Data.Text (Text)
48import Data.Text (Text)
50import qualified Data.Text as T
49import qualified Data.Text as T
50+import qualified Data.Text.Encoding as E
51import GHC.Exts (Constraint)
51import GHC.Exts (Constraint)
52import GHC.TypeLits (KnownSymbol, symbolVal)
52import GHC.TypeLits (KnownSymbol, symbolVal)
53−import Servant.API ((:<|>)(..),(:>), BasicAuth,
53+import Servant.API ((:<|>) (..), (:>), BasicAuth,
54− BasicAuthData, BuildHeadersTo(..),
54+ BasicAuthData, BuildHeadersTo (..),
55− Capture, contentType, Header,
55+ Capture, Header, Headers (..),
56− Headers(..), HttpVersion, IsSecure,
56+ HttpVersion, IsSecure,
57− MimeRender(..), MimeUnrender,
57+ MimeRender (..), MimeUnrender,
58 NoContent, QueryFlag, QueryParam,
58 NoContent, QueryFlag, QueryParam,
59− QueryParams, Raw, ReflectMethod(..),
59+ QueryParams, Raw, ReflectMethod (..),
60 RemoteHost, ReqBody,
60 RemoteHost, ReqBody,
61− ToHttpApiData(..), Vault, Verb)
61+ ToHttpApiData (..), Vault, Verb,
62+ contentType)
63+import Servant.API.Description (Summary)
62import qualified Servant.Auth as Auth
64import qualified Servant.Auth as Auth
63
65
64import Reflex.Dom.Core (Dynamic, Event, Reflex,
66import Reflex.Dom.Core (Dynamic, Event, Reflex,
65− XhrRequest(..),
67+ XhrRequest (..), XhrResponse (..),
66− XhrResponseHeaders(..),
68+ XhrResponseHeaders (..),
67− XhrResponse(..), attachPromptlyDynWith, constDyn, ffor, fmapMaybe,
69+ attachPromptlyDynWith, constDyn, ffor,
70+ fmapMaybe, leftmost,
68− leftmost, performRequestsAsync,
71+ performRequestsAsync)
69− )
70------------------------------------------------------------------------------
72------------------------------------------------------------------------------
71import Servant.Common.BaseUrl (BaseUrl(..), Scheme(..), baseUrlWidget,
73import Servant.Common.BaseUrl (BaseUrl(..), Scheme(..), baseUrlWidget,
72 showBaseUrl,
74 showBaseUrl,
@@ -101,7 +103,7 @@ import Servant.Common.Req (ClientOptions(..),
101-- >
103-- >
102-- > getAllBooks :: Event t l -> m (Event t (l, ReqResult [Book]))
104-- > getAllBooks :: Event t l -> m (Event t (l, ReqResult [Book]))
103-- > postNewBook :: Dynamic t (Maybe Book) -> Event t l
105-- > postNewBook :: Dynamic t (Maybe Book) -> Event t l
104−-- -> m (Event t (l, ReqResult Book)))
106+-- > -> m (Event t (l, ReqResult Book)))
105-- > (getAllBooks :<|> postNewBook) = client myApi host
107-- > (getAllBooks :<|> postNewBook) = client myApi host
106-- > where host = constDyn $ BaseUrl Http "localhost" 8080
108-- > where host = constDyn $ BaseUrl Http "localhost" 8080
107client
109client
@@ -226,22 +228,26 @@ instance {-# OVERLAPPING #-}
226
228
227toHeaders :: BuildHeadersTo ls => ReqResult tag a -> ReqResult tag (Headers ls a)
229toHeaders :: BuildHeadersTo ls => ReqResult tag a -> ReqResult tag (Headers ls a)
228toHeaders r =
230toHeaders r =
229− let toBS = E.encodeUtf8
230− hdrs = maybe []
231+ let hdrs = maybe []
231− (\xhr -> fmap (\(h,v) -> (mk (toBS h), toBS v))
232+ (\xhr -> fmap (\(h,v) -> (CI.map E.encodeUtf8 h, E.encodeUtf8 v))
232 (Map.toList $ _xhrResponse_headers xhr))
233 (Map.toList $ _xhrResponse_headers xhr))
233 (response r)
234 (response r)
234 in ffor r $ \a -> Headers {getResponse = a ,getHeadersHList = buildHeadersTo hdrs}
235 in ffor r $ \a -> Headers {getResponse = a ,getHeadersHList = buildHeadersTo hdrs}
235
236
237+
236class BuildHeaderKeysTo hs where
238class BuildHeaderKeysTo hs where
237− buildHeaderKeysTo :: Proxy hs -> [T.Text]
239+ buildHeaderKeysTo :: Proxy hs -> [CI.CI T.Text]
238
240
239instance {-# OVERLAPPABLE #-} BuildHeaderKeysTo '[]
241instance {-# OVERLAPPABLE #-} BuildHeaderKeysTo '[]
240 where buildHeaderKeysTo _ = []
242 where buildHeaderKeysTo _ = []
241
243
242instance {-# OVERLAPPABLE #-} (BuildHeaderKeysTo xs, KnownSymbol h)
244instance {-# OVERLAPPABLE #-} (BuildHeaderKeysTo xs, KnownSymbol h)
243 => BuildHeaderKeysTo ((Header h v) ': xs) where
245 => BuildHeaderKeysTo ((Header h v) ': xs) where
246+ buildHeaderKeysTo _ =
247+ let
248+ thisKey = CI.mk $ T.pack (symbolVal (Proxy :: Proxy h))
244− buildHeaderKeysTo _ = T.pack (symbolVal (Proxy :: Proxy h)) : buildHeaderKeysTo (Proxy :: Proxy xs)
249+ in thisKey : buildHeaderKeysTo (Proxy :: Proxy xs)
250+
245
251
246-- HEADERS Verb (Content) --
252-- HEADERS Verb (Content) --
247-- Headers combinator not treated in fully general case,
253-- Headers combinator not treated in fully general case,
@@ -323,6 +329,19 @@ instance HasClient t m sublayout tag
323 clientWithRouteAndResultHandler Proxy =
329 clientWithRouteAndResultHandler Proxy =
324 clientWithRouteAndResultHandler (Proxy :: Proxy sublayout)
330 clientWithRouteAndResultHandler (Proxy :: Proxy sublayout)
325
331
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+
326
345
327-- | If you use a 'QueryParam' in one of your endpoints in your API,
346-- | If you use a 'QueryParam' in one of your endpoints in your API,
328-- the corresponding querying function will automatically take
347-- the corresponding querying function will automatically take
src/Servant/Reflex/Multi.hs+35−22
@@ -37,28 +37,30 @@ module Servant.Reflex.Multi (
37 ) where
37 ) where
38
38
39------------------------------------------------------------------------------
39------------------------------------------------------------------------------
40−import Control.Applicative (liftA2)
40+import Control.Applicative (liftA2)
41−import Data.Functor.Compose (Compose (..), getCompose)
41+import Data.Functor.Compose (Compose (..), getCompose)
42−import Data.Proxy (Proxy (..))
42+import Data.Proxy (Proxy (..))
43−import qualified Data.Set as Set
43+import qualified Data.Set as Set
44−import Data.Text (Text)
44+import Data.Text (Text)
45−import qualified Data.Text as T
45+import qualified Data.Text as T
46−import qualified Data.Text.Encoding as E
46+import qualified Data.Text.Encoding as E
47−import GHC.TypeLits (KnownSymbol, symbolVal)
47+import GHC.TypeLits (KnownSymbol, symbolVal)
48−import Servant.API ((:<|>) (..), (:>), BasicAuth,
48+import Servant.API ((:<|>) (..), (:>), BasicAuth,
49− BasicAuthData, BuildHeadersTo (..),
49+ BasicAuthData, BuildHeadersTo (..),
50− Capture, Header, Headers (..),
50+ Capture, Header, Headers (..),
51− HttpVersion, IsSecure, MimeRender (..),
51+ HttpVersion, IsSecure,
52+ MimeRender (..), MimeUnrender,
52− MimeUnrender, NoContent, QueryFlag,
53+ NoContent, QueryFlag, QueryParam,
53− QueryParam, QueryParams, Raw,
54+ QueryParams, Raw, ReflectMethod (..),
54− ReflectMethod (..), RemoteHost,
55+ RemoteHost, ReqBody,
55− ReqBody, ToHttpApiData (..), Vault,
56+ ToHttpApiData (..), Vault, Verb,
56− Verb, contentType)
57+ contentType)
57−
58+import Servant.API.Description (Summary)
59+
58−import Reflex.Dom.Core (Dynamic, Event, Reflex,
60+import Reflex.Dom.Core (Dynamic, Event, Reflex,
59− XhrRequest (..),
61+ XhrRequest (..),
60− XhrResponseHeaders (..),
62+ XhrResponseHeaders (..),
61− attachPromptlyDynWith, constDyn)
63+ attachPromptlyDynWith, constDyn)
62------------------------------------------------------------------------------
64------------------------------------------------------------------------------
63import Servant.Common.BaseUrl (BaseUrl (..), Scheme (..),
65import Servant.Common.BaseUrl (BaseUrl (..), Scheme (..),
64 SupportsServantReflex)
66 SupportsServantReflex)
@@ -240,6 +242,17 @@ instance HasClientMulti t m sublayout f tag
240 clientWithRouteMulti (Proxy :: Proxy sublayout) q f tag
242 clientWithRouteMulti (Proxy :: Proxy sublayout) q f tag
241
243
242
244
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+
243------------------------------------------------------------------------------
256------------------------------------------------------------------------------
244instance (KnownSymbol sym,
257instance (KnownSymbol sym,
245 ToHttpApiData a,
258 ToHttpApiData a,
src/Servant/Reflex/RequestAPI.hsdeleted−567
@@ -1,567 +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 #-} -- Temporary
13−{-# LANGUAGE ScopedTypeVariables #-}
14−{-# LANGUAGE TupleSections #-}
15−{-# LANGUAGE TypeApplications #-}
16−{-# LANGUAGE TypeFamilies #-}
17−{-# LANGUAGE TypeOperators #-}
18−{-# LANGUAGE UndecidableInstances #-}
19−
20−module Servant.Reflex.RequestAPI where
21−
22−import Control.Arrow (first, second)
23−import Control.Lens
24−import Data.Generics.Product
25−import Data.Proxy
26−import Data.CaseInsensitive (mk)
27−import qualified Data.List as L
28−import qualified Data.Map as M
29−import Data.Maybe (catMaybes)
30−import Data.Semigroup ((<>))
31−import qualified Data.Set as Set
32−import qualified Data.Text.Encoding as E
33−import Data.Text (Text)
34−import qualified Data.Text as T
35−import qualified Data.ByteString.Lazy as BL
36−import GHC.Generics
37−import GHC.TypeLits (KnownSymbol, symbolVal)
38−import Reflex hiding (HList(..))
39−import Language.Javascript.JSaddle (MonadJSM)
40−import Servant.API hiding (HList(..))
41−import Servant.Common.Req hiding (QueryPart(..))
42−import Servant.Common.BaseUrl
43−
44−import Reflex.Dom hiding (HList(..))
45−
46−import Servant.Reflex (BuildHeaderKeysTo (..), toHeaders)
47−
48−{-
49−TODO:
50− - [x] :<|>
51− - [x] Verb content
52− - [x] Verb nocontent
53− - [x] Verb (Headers content)
54− - [x] Verb (Headers nocontent)
55− - [ ] Stream
56− - [X] Capture :> ...
57− - [ ] Capture' :> ...
58− - [x] CaptureAll :> ...
59− - [x] QueryParam :> ...
60− - [x] QueryParams :> ...
61− - [x] QueryFlag :> ...
62− - [x] ReqBody :> ...
63− - [ ] Header' :> ...
64− - [x] Header :> ...
65− - [x] Summary :> ...
66− - [x] Description :> ...
67− - [x] path :> ...
68− - [ ] RemoteHost :> ...
69− - [x] IsSecure :> ...
70− - [x] Vault :> ...
71− - [ ] WithNamedContext :> ...
72− - [ ] AuthenticatedRequest :> ...
73− - [x] BasicAuth :> ...
74− - [ ] EmptyClient :> ...
75− - [ ] EmptyClient
76− - [ ] HttpVersion :> ...
77−-}
78−data HList xs where
79− HNil :: HList '[]
80− HCons :: x -> HList xs -> HList (x ': xs)
81−
82−clientR :: forall t m layout f . (HasClientR t m (Seal layout) f, Applicative f, Reflex t)
83− => Proxy layout -> Proxy (m :: * -> *) -> Proxy t -> Proxy f
84− -> Dynamic t BaseUrl -> ClientR t m (Seal layout) f
85−clientR _ mP tP fP url =
86− clientRWithRoute (Proxy :: Proxy (Seal layout)) mP fP undefined url undefined
87−
88−-- | A class for generating top-level the API client function
89−class HasClientR t (m :: * -> *) api (f :: * -> *) where
90− type ClientR t m api f :: *
91− clientRWithRoute :: Proxy api -> Proxy m -> Proxy f
92− -> Dynamic t (f (Req t)) -> Dynamic t BaseUrl
93− -> ClientOptions -> ClientR t m api f
94−
95−-- instance EndpointR t m Raw f where
96−-- type EInputs t m Raw f = '[XhrRequest T.Text]
97−-- type EOutput t m Raw f = XhrResponse
98−
99−-- mkReq (HCons (XhrRequest method url conf) NHil) =
100−-- ReqR method
101−
102−
103−-- | A class for generating endpoint-level API client functions
104−-- We need two separate type classes because the needs of endpoints
105−-- and top-level clients are incompatible
106−--
107−-- Endpoints need to accumulate a list of their input types from
108−-- the API definition (the @EInputs@ associated type) when building
109−-- up a function from a sequence of @:>@ combinators. But for
110−-- sequences of @:<|>@ combinators, such a listing doesn't make
111−-- sense. So, we use one class for endpoint-level recursion and
112−-- another for collecting endpoints into the top-level API
113−class EndpointR t m api f where
114−
115− type EInputs t m api f :: [*]
116− type EOutput t m api f :: *
117−
118− mkReq :: HList (EInputs t m api f) -> ReqR
119− getResp :: XhrResponse -> EOutput t m api f
120−
121−
122−------------------------------------------------------------------------------
123−-- | :<|> APIs
124−instance {-# OVERLAPPING #-}
125− (HasClientR t m (layoutLeft) f,
126− HasClientR t m (layoutRight) f
127− ) => HasClientR t m (layoutLeft :<|> layoutRight) f where
128− type ClientR t m (layoutLeft :<|> layoutRight) f =
129− ClientR t m layoutLeft f :<|> ClientR t m layoutRight f
130− clientRWithRoute _ tP mP _ url opts =
131− clientRWithRoute (Proxy @layoutLeft) tP mP undefined url opts
132− :<|>
133− clientRWithRoute (Proxy @layoutRight) tP mP undefined url opts
134−
135−#if MIN_VERSION_servant (0,13,0)
136−-- TODO: Untested
137−instance HasClientR t m EmptyClient f where
138− type ClientR t m api f = EmptyClient
139− clientRWithRoute _ _ _ _ _ _ = EmptyClient
140−#endif
141−
142−
143−class ToTuple xs where
144− type Tuple xs :: *
145− toTuple :: xs -> Tuple xs
146− fromTuple :: Tuple xs -> xs
147−
148−data Sealed a
149−data SealedRaw
150−
151−type family Seal a where
152− Seal (a :<|> b) = Seal a :<|> Seal b
153− Seal Raw = SealedRaw
154− Seal a = Sealed a
155−
156−
157−-- | Turn a single Endpoint into a client, by turning the input HList
158−-- into a tuple
159−instance {-# OVERLAPPING #-} forall t m layout f .(EndpointR t m layout f,
160− ToTuple (HList (EInputs t m layout f)),
161− Monad m
162− , Traversable f
163− , SupportsServantReflex t m
164− ) => HasClientR t (m :: * -> *) (Sealed layout) (f :: * -> *) where
165− type ClientR t m (Sealed layout) f =
166− Event t (f (Tuple (HList (EInputs t m layout f))))
167− -> m (Event t (f (EOutput t m layout f)))
168− clientRWithRoute _ mP fP dReq url opts (reqs :: Event t (f (Tuple (HList (EInputs t m layout f ))))) = do
169− let reqTuples = fmap (fromTuple @(HList (EInputs t m layout f ))) <$> reqs
170−
171− -- TODO: replace these 2 lines w/ the next 2. The trace is for library debugging
172− reqRs = traceEventWith (concat . fmap ((<> "\n") . show)) $ fmap (mkReq @t @m @layout @f) <$> reqTuples
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 <- performRequestsAsync $ reqs'
177− return (fmap (getResp @t @m @layout @f) <$> resps)
178−
179−
180−instance (SupportsServantReflex t m, Traversable f) => HasClientR t m SealedRaw f where
181− type ClientR t m SealedRaw f = Event t (f (XhrRequest T.Text)) -> m (Event t (f XhrResponse))
182− clientRWithRoute _ mP fP dReq url opts reqs = performRequestsAsync reqs
183−
184−
185−instance {-# OVERLAPPING #-}(MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts), SupportsServantReflex t m, Applicative f
186− ) => EndpointR t m (Verb method status cts' a) f where
187− type EInputs t m (Verb method status cts' a) f = '[]
188− type EOutput t m (Verb method status cts' a) f = Either String a
189−
190− mkReq _ = emptyReqR . E.decodeUtf8 $ reflectMethod (Proxy @method)
191− getResp resp = case _xhrResponse_response resp of
192− Just (XhrResponseBody_ArrayBuffer x) -> mimeUnrender (Proxy @ct) (BL.fromStrict x)
193−
194−instance {-# OVERLAPPING #-}(ReflectMethod method, SupportsServantReflex t m, Applicative f
195− ) => EndpointR t m (Verb method status cts' NoContent) f where
196− type EInputs t m (Verb method status cts' NoContent) f = '[]
197− type EOutput t m (Verb method status cts' NoContent) f = Either String NoContent
198−
199− mkReq _ = emptyReqR . E.decodeUtf8 $ reflectMethod (Proxy @method)
200− getResp resp = Right NoContent -- TODO: Should EOutput be `NoContent` rather than Either... ?
201−
202−
203−instance {-# OVERLAPPING #-}
204− (MimeUnrender ct a,
205− BuildHeadersTo hs,
206− ReflectMethod method, cts' ~ (ct ': cts),
207− SupportsServantReflex t m,
208− Applicative f,
209− BuildHeaderKeysTo hs
210− ) => EndpointR t m (Verb method status cts' (Headers hs a)) f where
211− type EInputs t m (Verb method status cts' (Headers hs a)) f = '[]
212− type EOutput t m (Verb method status cts' (Headers hs a)) f = Either String (Headers hs a)
213−
214− mkReq _ = set (field @"reqRRespHeaders") (OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy @hs)))) $ emptyReqR $ E.decodeUtf8 $ reflectMethod (Proxy @method)
215− getResp resp = case _xhrResponse_response resp of
216− Just (XhrResponseBody_ArrayBuffer x) ->
217− let hs = buildHeadersTo
218− . fmap (first (mk . E.encodeUtf8) . second E.encodeUtf8)
219− . M.toList
220− $ _xhrResponse_headers resp
221− in fmap (flip Headers (hs)) $ mimeUnrender (Proxy @ct) (BL.fromStrict x)
222−
223−
224−instance {-# OVERLAPPING #-}(MimeUnrender ct a,
225− BuildHeadersTo hs,
226− BuildHeaderKeysTo hs,
227− ReflectMethod method,
228− SupportsServantReflex t m,
229− Applicative f
230− ) => EndpointR t m (Verb method status cts' (Headers hs NoContent)) f where
231− type EInputs t m (Verb method status cts' (Headers hs NoContent)) f = '[]
232− type EOutput t m (Verb method status cts' (Headers hs NoContent)) f = Either String (Headers hs NoContent)
233−
234− mkReq _ = set (field @"reqRRespHeaders") (OnlyHeaders (Set.fromList (buildHeaderKeysTo (Proxy @hs)))) $ emptyReqR . E.decodeUtf8 $ reflectMethod (Proxy @method)
235− getResp resp = case _xhrResponse_response resp of
236− Just (XhrResponseBody_ArrayBuffer x) ->
237− let hs = buildHeadersTo
238− . fmap (first (mk . E.encodeUtf8) . second E.encodeUtf8)
239− . M.toList
240− $ _xhrResponse_headers resp
241− in Right (Headers NoContent hs)
242−
243−
244−instance (SupportsServantReflex t m,
245− ToHttpApiData a,
246− EndpointR t m sublayout f,
247− Applicative f
248− ) => EndpointR t m (Capture capture a :> sublayout) f where
249−
250− type EInputs t m (Capture capture a :> sublayout) f = a ': EInputs t m sublayout f
251− type EOutput t m (Capture capture a :> sublayout) f = EOutput t m sublayout f
252−
253− mkReq (HCons x xs) = (\a -> over (field @"reqRPathParts") (toUrlPiece a:)) x $
254− mkReq @t @m @sublayout @f xs
255−
256− getResp = getResp @t @m @sublayout @f
257−
258−instance (SupportsServantReflex t m,
259− ToHttpApiData a,
260− EndpointR t m sublayout f,
261− Applicative f
262− ) => EndpointR t m (CaptureAll capture a :> sublayout) f where
263−
264− type EInputs t m (CaptureAll capture a :> sublayout) f = [a] ': EInputs t m sublayout f
265− type EOutput t m (CaptureAll capture a :> sublayout) f = EOutput t m sublayout f
266−
267− mkReq (HCons x xs) = (\a -> over (field @"reqRPathParts") (fmap toUrlPiece a <>)) x $
268− mkReq @t @m @sublayout @f xs
269−
270− getResp = getResp @t @m @sublayout @f
271−
272−
273−instance (SupportsServantReflex t m,
274− KnownSymbol sym,
275− EndpointR t m sublayout f,
276− Applicative f
277− ) => EndpointR t m (sym :> sublayout) f where
278−
279− type EInputs t m (sym :> sublayout) f = EInputs t m sublayout f
280− type EOutput t m (sym :> sublayout) f = EOutput t m sublayout f
281−
282− mkReq r = over (field @"reqRPathParts") (T.pack (symbolVal (Proxy @sym)) :) $ mkReq @t @m @sublayout @f r
283−
284− getResp = getResp @t @m @sublayout @f
285−
286−instance (SupportsServantReflex t m,
287− ToHttpApiData a,
288− EndpointR t m sublayout f,
289− KnownSymbol sym,
290− Applicative f
291− ) => EndpointR t m (QueryParam sym a :> sublayout) f where
292−
293− type EInputs t m (QueryParam sym a :> sublayout) f = Maybe a ': EInputs t m sublayout f
294− type EOutput t m (QueryParam sym a :> sublayout) f = EOutput t m sublayout f
295−
296− mkReq (HCons x xs) = over (field @"reqRParams") ((T.pack $ symbolVal (Proxy @sym),
297− QueryPartParam $ fmap (toQueryParam) x) : ) $
298− mkReq @t @m @sublayout @f xs
299−
300−
301− getResp = getResp @t @m @sublayout @f
302−
303−instance (SupportsServantReflex t m,
304− ToHttpApiData a,
305− EndpointR t m sublayout f,
306− KnownSymbol sym,
307− Applicative f
308− ) => EndpointR t m (QueryParams sym a :> sublayout) f where
309−
310− type EInputs t m (QueryParams sym a :> sublayout) f = [a] ': EInputs t m sublayout f
311− type EOutput t m (QueryParams sym a :> sublayout) f = EOutput t m sublayout f
312−
313− mkReq (HCons x xs) =
314− let newParams = (T.pack (symbolVal (Proxy @sym)), QueryPartParams (toQueryParam <$> x))
315− in over (field @"reqRParams") (newParams :) $ mkReq @t @m @sublayout @f xs
316−
317− getResp = getResp @t @m @sublayout @f
318−
319−instance (SupportsServantReflex t m,
320− EndpointR t m sublayout f,
321− KnownSymbol sym,
322− Applicative f
323− ) => EndpointR t m (QueryFlag sym :> sublayout) f where
324−
325− type EInputs t m (QueryFlag sym :> sublayout) f = Bool ': EInputs t m sublayout f
326− type EOutput t m (QueryFlag sym :> sublayout) f = EOutput t m sublayout f
327−
328− mkReq (HCons x xs) =
329− let newParam = (T.pack (symbolVal (Proxy @sym)), QueryPartFlag x)
330− in over (field @"reqRParams") (newParam :) $ mkReq @t @m @sublayout @f xs
331−
332− getResp = getResp @t @m @sublayout @f
333−
334−instance (SupportsServantReflex t m,
335− EndpointR t m sublayout f,
336− MimeRender ct a,
337− Applicative f
338− ) => EndpointR t m (ReqBody (ct ': cts) a :> sublayout) f where
339−
340−
341− type EInputs t m (ReqBody (ct ': cts) a :> sublayout) f = a ': EInputs t m sublayout f
342− type EOutput t m (ReqBody (ct ': cts) a :> sublayout) f = EOutput t m sublayout f
343−
344− mkReq (HCons x xs) =
345− set (field @"reqRBody") (Just (mimeRender (Proxy @ct) x, T.pack (show (contentType (Proxy @ct))))) $ mkReq @t @m @sublayout @f xs
346−
347− getResp = getResp @t @m @sublayout @f
348−
349−
350−instance (SupportsServantReflex t m,
351− EndpointR t m sublayout f,
352− KnownSymbol sym,
353− ToHttpApiData a,
354− Applicative f
355− ) => EndpointR t m (Header sym a :> sublayout) f where
356−
357−
358− type EInputs t m (Header sym a :> sublayout) f = a ': EInputs t m sublayout f
359− type EOutput t m (Header sym a :> sublayout) f = EOutput t m sublayout f
360−
361− mkReq (HCons x xs) =
362− over (field @"reqRHeaders")
363− -- ((symbolVal (Proxy @sym), (mimeRender (Proxy @a) x)) :)
364− ((T.pack $ symbolVal (Proxy @sym), E.decodeUtf8 $ toHeader x) :)
365− $ mkReq @t @m @sublayout @f xs
366−
367− getResp = getResp @t @m @sublayout @f
368−
369−#if MIN_VERSION_servant(0,13,0)
370−-- TODO: Untested
371−instance (SupportsServantReflex t m,
372− EndpointR t m sublayout f,
373− KnownSymbol sym,
374− ToHttpApiData a,
375− Applicative f
376− ) => EndpointR t m (Summary desc :> sublayout) f where
377−
378− type EInputs t m (Summary desc :> sublayout) f = EInputs t m sublayout f
379− type EOutput t m (Summary desc :> sublayout) f = EOutput t m sublayout f
380−
381− mkReq = mkReq @t @m @sublayout @f
382−
383− getResp = getResp @t @m @sublayout @f
384−#endif
385−
386−
387−#if MIN_VERSION_servant(0,13,0)
388−-- TODO: Untested
389−instance (SupportsServantReflex t m,
390− EndpointR t m sublayout f,
391− KnownSymbol sym,
392− ToHttpApiData a,
393− Applicative f
394− ) => EndpointR t m (Description desc :> sublayout) f where
395−
396− type EInputs t m (Descriptiok desc :> sublayout) f = EInputs t m sublayout f
397− type EOutput t m (Description desc :> sublayout) f = EOutput t m sublayout f
398−
399− mkReq = mkReq @t @m @sublayout @f
400−
401− getResp = getResp @t @m @sublayout @f
402−#endif
403−
404−instance (SupportsServantReflex t m,
405− EndpointR t m sublayout f,
406− KnownSymbol sym,
407− ToHttpApiData a,
408− Applicative f
409− ) => EndpointR t m (IsSecure :> sublayout) f where
410−
411−
412− type EInputs t m (IsSecure :> sublayout) f = EInputs t m sublayout f
413− type EOutput t m (IsSecure :> sublayout) f = EOutput t m sublayout f
414−
415− mkReq = mkReq @t @m @sublayout @f
416−
417− getResp = getResp @t @m @sublayout @f
418−
419−instance (SupportsServantReflex t m,
420− EndpointR t m sublayout f,
421− KnownSymbol sym,
422− ToHttpApiData a,
423− Applicative f
424− ) => EndpointR t m (Vault :> sublayout) f where
425−
426−
427− type EInputs t m (Vault :> sublayout) f = EInputs t m sublayout f
428− type EOutput t m (Vault :> sublayout) f = EOutput t m sublayout f
429−
430− mkReq = mkReq @t @m @sublayout @f
431−
432− getResp = getResp @t @m @sublayout @f
433−
434−instance (SupportsServantReflex t m,
435− EndpointR t m sublayout f,
436− Applicative f
437− ) => EndpointR t m (BasicAuth realm usr :> sublayout) f where
438−
439− type EInputs t m (BasicAuth realm usr :> sublayout) f = BasicAuthData ': EInputs t m sublayout f
440− type EOutput t m (BasicAuth realm usr :> sublayout) f = EOutput t m sublayout f
441−
442− mkReq (HCons x xs) =
443− set (field @"reqRAuthData") (Just x) $ mkReq @t @m @sublayout @f xs
444−
445− getResp = getResp @t @m @sublayout @f
446−
447−
448−instance ToTuple (HList '[]) where
449− type Tuple (HList '[]) = ()
450− toTuple _ = ()
451− fromTuple _ = HNil
452−
453−instance ToTuple (HList '[a]) where
454− type Tuple (HList '[a]) = a
455− toTuple (HCons a HNil) = a
456− fromTuple a = HCons a HNil
457−
458−instance ToTuple (HList '[a,b]) where
459− type Tuple (HList '[a,b]) = (a,b)
460− toTuple (HCons a (HCons b HNil)) = (a,b)
461− fromTuple (a,b) = HCons a (HCons b HNil)
462−
463−instance ToTuple (HList '[a,b,c]) where
464− type Tuple (HList '[a,b,c]) = (a,b,c)
465− toTuple (HCons a (HCons b (HCons c HNil))) = (a,b,c)
466− fromTuple (a,b,c) = HCons a (HCons b (HCons c HNil))
467−
468−type MyAPI = Header "hi" Int :> Capture "hello" Int :> Capture "hello2" Bool :> Get '[JSON] (Headers '[Header "hi" Int] Int)
469− :<|> Get '[JSON] Bool
470−
471−type MyAPI2 = Raw
472−
473−
474−m :: forall t m.MonadWidget t m => m ()
475−m = do
476− let (a :<|> b) = clientR (Proxy @MyAPI) (Proxy :: Proxy m) (Proxy :: Proxy t)
477− (Proxy @[]) undefined
478− btn <- button "Hello"
479− resA <- a (fmap (:[]) ((2, 5,True) <$ btn))
480− resB <- b (fmap (:[]) (() <$ btn))
481− display =<< holdDyn "waiting" (leftmost [show <$> resB ])
482− -- [show <$> resA
483− -- , show <$> resB
484− -- ])
485−
486− let rawR = clientR (Proxy @MyAPI2) (Proxy @m) (Proxy @t) (Proxy @[]) undefined
487− return ()
488−
489−data ReqR = ReqR
490− { reqRMethod :: T.Text
491− , reqRPathParts :: [T.Text]
492− , reqRParams :: [(T.Text, QueryPart)]
493− , reqRBody :: Maybe (BL.ByteString, T.Text)
494− , reqRHeaders :: [(T.Text, T.Text)]
495− , reqRRespHeaders :: XhrResponseHeaders
496− , reqRAuthData :: Maybe BasicAuthData
497− } deriving (Generic)
498−
499−instance Show ReqR where
500− show (ReqR m pp ps b hs hs' a) = concat $ L.intersperse "\n" [
501− "ReqR {"
502− , ("reqRMethod = " <> show m)
503− , ("reqRPathParts = " <> show pp)
504− , ("reqRParams = " <> show ps)
505− , ("reqRBody = " <> show b)
506− , ("reqRHeaders = " <> show hs)
507− , ("reqRRespHeaders = " <> show hs')
508− , ("reqRAuthData = " <> maybe "Nothing" (const "Just <<redacted>>") a)
509− ]
510−
511−emptyReqR :: T.Text -> ReqR
512−emptyReqR method = ReqR method [] [] Nothing [] (OnlyHeaders mempty) Nothing
513−
514−data QueryPart = QueryPartParam (Maybe Text)
515− | QueryPartParams [Text]
516− | QueryPartFlag Bool
517− deriving (Show)
518−
519−mkXhrRequest :: BaseUrl -> ReqR -> XhrRequest T.Text
520−mkXhrRequest reqHost req =
521− let path = T.intercalate "/" $ reqRPathParts req
522−
523− queryPartString :: (Text, QueryPart) -> Maybe Text
524− queryPartString (pName, qp) = case qp of
525−
526− QueryPartParam Nothing -> Nothing
527− QueryPartParam (Just a) -> Just (pName <> "=" <> escape a)
528−
529− QueryPartParams ps -> Just . T.intercalate "&"
530− $ fmap (\p -> pName <> "=" <> escape p)
531− ps
532−
533− QueryPartFlag True -> Just pName
534− QueryPartFlag False -> Nothing
535−
536− (</>) :: Text -> Text -> Text
537− x </> y | ("/" `T.isSuffixOf` x) || ("/" `T.isPrefixOf` y) = x <> y
538− | otherwise = x <> "/" <> y
539−
540− queryString = T.intercalate "&" . catMaybes . map queryPartString $ reqRParams req
541− url = showBaseUrl reqHost </> path <> if T.null queryString then "" else (T.cons '?' queryString)
542−
543− (headers, body) =
544− let headers0 = M.fromList $ reqRHeaders req
545− in case reqRBody req of
546− Nothing -> (headers0, "")
547− Just (rb,ct) -> (M.insert "Content-Type" ct headers0,
548− E.decodeUtf8 $ BL.toStrict rb)
549−
550− xhrReqConfig = def
551− { _xhrRequestConfig_headers = headers
552− , _xhrRequestConfig_user = E.decodeUtf8 . basicAuthUsername <$> reqRAuthData req
553− , _xhrRequestConfig_password = E.decodeUtf8 . basicAuthPassword <$> reqRAuthData req
554− , _xhrRequestConfig_responseType = Just XhrResponseType_ArrayBuffer
555− , _xhrRequestConfig_sendData = body
556− , _xhrRequestConfig_responseHeaders = reqRRespHeaders req
557− , _xhrRequestConfig_withCredentials = False
558− }
559− xhrReq = XhrRequest (reqRMethod req) url xhrReqConfig
560− in xhrReq
561−
562−transformExample :: MonadWidget t m => ((Int, Char) -> Performable m Bool) -> Event t (Int, Char) -> m (Event t Bool)
563−transformExample f = \triggers -> performEvent (f <$> triggers)
564−-- transformExample = undefined -- let x = performEvent :: _ in undefined
565−
566−transform :: MonadWidget t m => forall a. (inp -> IO outp) -> (Event t inp -> m (Event t outp))
567−transform = undefined
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,
20import qualified Data.Text as T
20import qualified Data.Text as T
21import qualified Data.Text.IO as T
21import qualified Data.Text.IO as T
22import GHC.Generics
22import GHC.Generics
23−import Snap.Core hiding (addHeader)
23+import Servant.Server.Internal.SnapShims
24+import Snap.Core
24import Snap.Http.Server
25import Snap.Http.Server
25
26
26import Servant
27import Servant
27−import Servant.Server ()
28+import Servant.Server
28−import System.Directory
29-- import Snap.Util.FileServe
29-- import Snap.Util.FileServe
30import System.Environment (lookupEnv)
30import System.Environment (lookupEnv)
31import API
31import API
32−import Snap hiding (addHeader)
32+import Snap
33
33
34-- * Example
34-- * Example
35
35
@@ -52,7 +52,7 @@ data App = App { appStaticDir :: String }
52--
52--
53-- Each handler runs in the 'ExceptT ServantErr IO' monad.
53-- Each handler runs in the 'ExceptT ServantErr IO' monad.
54server :: Server API '[BasicAuthCheck (Handler App App) ()] (Handler App App)
54server :: Server API '[BasicAuthCheck (Handler App App) ()] (Handler App App)
55−server = return () :<|> return 100 :<|> dblint :<|> sayhi :<|> dbl
55+server = return () :<|> return 100 :<|> sayhi :<|> dbl
56 :<|> multi :<|> qna :<|> serveSecret
56 :<|> multi :<|> qna :<|> serveSecret
57 :<|> statics
57 :<|> statics
58 where sayhi :: Maybe Text -> [Text] -> Bool -> Handler App App Text
58 where sayhi :: Maybe Text -> [Text] -> Bool -> Handler App App Text
@@ -66,16 +66,15 @@ server = return () :<|> return 100 :<|> dblint :<|> sayhi :<|> dbl
66 | otherwise = T.intercalate ", " (L.init greetings)
66 | otherwise = T.intercalate ", " (L.init greetings)
67 <> ", and " <> L.last greetings <> ", "
67 <> ", and " <> L.last greetings <> ", "
68 return . modifier $ greetPart <> n
68 return . modifier $ greetPart <> n
69− dblint = return . (* 2)
70 dbl x = if x `elem` [4,13]
69 dbl x = if x `elem` [4,13]
71 then throwError $ err500 { errBody = "No unlucky numbers please" }
70 then throwError $ err500 { errBody = "No unlucky numbers please" }
72 else return $ x * 2
71 else return $ x * 2
73 multi = return . bool "Box unchecked" "Box Checked"
72 multi = return . bool "Box unchecked" "Box Checked"
74− qna q nm fav = do
73+ qna q = do
75 liftIO $ do
74 liftIO $ do
76 putStrLn $ "qna got: " ++ show q
75 putStrLn $ "qna got: " ++ show q
77 T.putStrLn $ unQuestion q
76 T.putStrLn $ unQuestion q
78− return $ addHeader (fromMaybe 10 nm) $ addHeader (fromMaybe False fav) $ (Answer $ unQuestion q)
77+ return $ Answer $ unQuestion q
79 serveSecret _ = do
78 serveSecret _ = do
80 req <- getRequest
79 req <- getRequest
81 liftIO $ putStrLn (show req)
80 liftIO $ putStrLn (show req)
@@ -100,7 +99,4 @@ initApp = makeSnaplet "myapp" "example" Nothing $ do
100
99
101-- Put this all to work!
100-- Put this all to work!
102main :: IO ()
101main :: IO ()
103−main = commandLineConfig emptyConfig >>= \cfg -> print cfg >> serveSnaplet cfg initApp
102+main = serveSnaplet mempty initApp
104−-- main = serveSnaplet (defaultConfig { accessLog = "/tmp/log/testserver-access.log"
105−-- , errorLog = "/tmp/log/testserver-error.log"
106−-- }) 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+3−3
@@ -19,13 +19,13 @@ executable back
19 main-is: Main.hs
19 main-is: Main.hs
20 -- other-modules:
20 -- other-modules:
21 -- other-extensions:
21 -- other-extensions:
22− build-depends: aeson >= 0.9 && < 1.4
22+ build-depends: aeson >= 0.9 && < 1.5
23− , base >=4.8 && <4.14
23+ , base >=4.8 && < 5
24 , mtl
24 , mtl
25 , snap >= 1.0 && < 1.2
25 , snap >= 1.0 && < 1.2
26 , snap-server >= 1.0 && < 1.2
26 , snap-server >= 1.0 && < 1.2
27 , snap-core >= 1.0 && < 1.1
27 , snap-core >= 1.0 && < 1.1
28− , servant >= 0.8 && < 0.15
28+ , servant >= 0.16 && < 0.18
29 , servant-snap >= 0.8 && < 0.9
29 , servant-snap >= 0.8 && < 0.9
30 , text >= 1.0 && < 1.3
30 , text >= 1.0 && < 1.3
31 -- hs-source-dirs:
31 -- hs-source-dirs:
travis.nix+1
@@ -48,6 +48,7 @@ in
48 inherit reflexPlatform ghcPkgs ghcjsPkgs;
48 inherit reflexPlatform ghcPkgs ghcjsPkgs;
49
49
50 build = ghcjsPkgs.servant-reflex;
50 build = ghcjsPkgs.servant-reflex;
51+ buildGhc = ghcPkgs.servant-reflex;
51 testserver = ghcPkgs.testserver;
52 testserver = ghcPkgs.testserver;
52 testdriver = ghcPkgs.testdriver;
53 testdriver = ghcPkgs.testdriver;
53 inherit testresults;
54 inherit testresults;