on master help sign in
clean up servant-js significantly, simplify the API of the library a lot and document @freezeboy's sweet generators from #159 a bit in the main module of the library, Servant.JS along with how people should use the library.
a2a4665Alp Mestanogullari
servant-examples/tutorial/T9.hs+1−1
@@ -93,7 +93,7 @@ server' = server
93 :<|> serveDirectory "tutorial/t9"
93 :<|> serveDirectory "tutorial/t9"
94
94
95apiJS :: String
95apiJS :: String
96−apiJS = jsForAPI api generateJQueryJS
96+apiJS = jsForAPI api jquery
97
97
98writeJSFiles :: IO ()
98writeJSFiles :: IO ()
99writeJSFiles = do
99writeJSFiles = do
servant-js/examples/counter.hs+31−30
@@ -12,8 +12,6 @@ import Network.Wai.Handler.Warp (run)
12import Servant
12import Servant
13import Servant.JS
13import Servant.JS
14import qualified Servant.JS as SJS
14import qualified Servant.JS as SJS
15−import qualified Servant.JS.Vanilla as JS
16−import qualified Servant.JS.JQuery as JQ
17import qualified Servant.JS.Angular as NG
15import qualified Servant.JS.Angular as NG
18import System.FilePath
16import System.FilePath
19
17
@@ -43,55 +41,58 @@ currentValue counter = liftIO $ readTVarIO counter
43-- * Our API type
41-- * Our API type
44type TestApi = "counter" :> Post '[JSON] Counter -- endpoint for increasing the counter
42type TestApi = "counter" :> Post '[JSON] Counter -- endpoint for increasing the counter
45 :<|> "counter" :> Get '[JSON] Counter -- endpoint to get the current value
43 :<|> "counter" :> Get '[JSON] Counter -- endpoint to get the current value
46− :<|> Raw -- used for serving static files
47
44
45+type TestApi' = TestApi
46+ :<|> Raw -- used for serving static files
47+
48+-- this proxy only targets the proper endpoints of our API,
49+-- not the static file serving bit
48testApi :: Proxy TestApi
50testApi :: Proxy TestApi
49testApi = Proxy
51testApi = Proxy
50
52
53+-- this proxy targets everything
54+testApi' :: Proxy TestApi'
55+testApi' = Proxy
56+
51-- * Server-side handler
57-- * Server-side handler
52
58
53-- where our static files reside
59-- where our static files reside
54www :: FilePath
60www :: FilePath
55www = "examples/www"
61www = "examples/www"
56
62
57−-- defining handlers
63+-- defining handlers of our endpoints
58server :: TVar Counter -> Server TestApi
64server :: TVar Counter -> Server TestApi
59server counter = counterPlusOne counter -- (+1) on the TVar
65server counter = counterPlusOne counter -- (+1) on the TVar
60 :<|> currentValue counter -- read the TVar
66 :<|> currentValue counter -- read the TVar
67+
68+-- the whole server, including static file serving
69+server' :: TVar Counter -> Server TestApi'
70+server' counter = server counter
61− :<|> serveDirectory www -- serve static files
71+ :<|> serveDirectory www -- serve static files
62
72
63runServer :: TVar Counter -- ^ shared variable for the counter
73runServer :: TVar Counter -- ^ shared variable for the counter
64 -> Int -- ^ port the server should listen on
74 -> Int -- ^ port the server should listen on
65 -> IO ()
75 -> IO ()
66−runServer var port = run port (serve testApi $ server var)
76+runServer var port = run port (serve testApi' $ server' var)
67−
77+
68−-- * Generating the JQuery code
78+writeServiceJS :: FilePath -> IO ()
69−incCounterJS :: AjaxReq
79+writeServiceJS fp =
70−currentValueJS :: AjaxReq
80+ writeJSForAPI testApi
71−incCounterJS :<|> currentValueJS :<|> _ = javascript testApi
72−
73−writeJS :: JavaScriptGenerator -> FilePath -> [AjaxReq] -> IO ()
74−writeJS gen fp functions = writeFile fp $
75− concatMap (\req -> generateJS req gen) functions
76−
77−writeServiceJS :: FilePath -> [AjaxReq] -> IO ()
78−writeServiceJS fp functions = writeFile fp $
79− NG.wrapInServiceWith (NG.defAngularOptions { NG.serviceName = "counterSvc" })
81+ (angularServiceWith (NG.defAngularOptions { NG.serviceName = "counterSvc" })
80− (defCommonGeneratorOptions { SJS.moduleName = "counterApp" }) functions
82+ (defCommonGeneratorOptions { SJS.moduleName = "counterApp" })
83+ )
84+ fp
81
85
82main :: IO ()
86main :: IO ()
83main = do
87main = do
84 -- write the JS code to www/api.js at startup
88 -- write the JS code to www/api.js at startup
85− writeJS JQ.generateJQueryJS (www </> "jquery" </> "api.js")
89+ writeJSForAPI testApi jquery (www </> "jquery" </> "api.js")
86− [ incCounterJS, currentValueJS ]
90+
87− writeJS JS.generateVanillaJS (www </> "vanilla" </> "api.js")
91+ writeJSForAPI testApi vanillaJS (www </> "vanilla" </> "api.js")
88− [ incCounterJS, currentValueJS ]
92+
89− writeJS (NG.generateAngularJS
90− NG.defAngularOptions) (www </> "angular" </> "api.js")
93+ writeJSForAPI testApi (angular defAngularOptions) (www </> "angular" </> "api.js")
91− [ incCounterJS, currentValueJS ]
94+
92− writeServiceJS
93− (www </> "angular" </> "api.service.js")
95+ writeServiceJS (www </> "angular" </> "api.service.js")
94− [ incCounterJS, currentValueJS ]
95
96
96 -- setup a shared counter
97 -- setup a shared counter
97 cnt <- newCounter
98 cnt <- newCounter
servant-js/servant-js.cabal+1−1
@@ -38,7 +38,7 @@ library
38 Servant.JS.Angular
38 Servant.JS.Angular
39 Servant.JS.JQuery
39 Servant.JS.JQuery
40 Servant.JS.Vanilla
40 Servant.JS.Vanilla
41− other-modules: Servant.JS.Internal
41+ Servant.JS.Internal
42 build-depends: base >=4.5 && <5
42 build-depends: base >=4.5 && <5
43 , charset
43 , charset
44 , lens >= 4
44 , lens >= 4
servant-js/src/Servant/JS.hs+118−32
@@ -4,56 +4,140 @@
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
5-----------------------------------------------------------------------------
5-----------------------------------------------------------------------------
6-- |
6-- |
7−-- Module : Servant.JQuery
7+-- Module : Servant.JS
8-- License : BSD3
8-- License : BSD3
9-- Maintainer : Alp Mestanogullari <alpmestan@gmail.com>
9-- Maintainer : Alp Mestanogullari <alpmestan@gmail.com>
10-- Stability : experimental
10-- Stability : experimental
11-- Portability : non-portable
11-- Portability : non-portable
12+--
13+-- Generating Javascript code to query your APIs using vanilla Javascript,
14+-- Angular.js or JQuery.
15+--
16+-- Using this package is very simple. Say you have this API type around:
17+--
18+-- > type API = "users" :> Get '[JSON] [Users]
19+-- > :<|> "messages" :> Get '[JSON] [Message]
20+--
21+-- All you need to do to generate the Javascript code is to write a 'Proxy'
22+-- for this API type:
23+--
24+-- > api :: Proxy API
25+-- > api = Proxy
26+--
27+-- And pick one of the generators:
28+--
29+-- - 'vanillaJS' and 'vanillaJSWith' generate functions that use
30+-- /XMLHttpRequest/ to query your endpoints. The former just calls
31+-- the latter with default code-generation options.
32+-- - 'jquery' and 'jqueryWith' follow the same pattern except that they
33+-- generate functions that use /jQuery/'s AJAX functions.
34+-- - 'angular' and 'angularWith' do the same but use /Angular.js/'s $http
35+-- service. In addition, we provide 'angularService' and 'angularServiceWith'
36+-- which produce functions under an Angular service that your controlers
37+-- can depend on to query the API.
38+--
39+-- Let's keep it simple and produce vanilla Javascript code with the default options.
40+--
41+-- @
42+-- jsCode :: String
43+-- jsCode = 'jsForAPI' api 'vanillaJS'
44+-- @
45+--
46+-- That's it! If you want to write that code to a file:
47+--
48+-- @
49+-- writeJSCode :: IO ()
50+-- writeJSCode = 'writeJSForAPI' api 'vanillaJS' "./my_api.js"
51+-- @
52+--
53+-- If you want to customize the rendering options, take a look
54+-- at 'CommonGeneratorOptions' which are generic options common to all the
55+-- generators. the /xxxWith/ variants all take 'CommonGeneratorOptions' whereas
56+-- the /xxx/ versions use 'defCommonGeneratorOptions'. Once you have some custom
57+--
58+-- > myOptions :: 'CommonGeneratorOptions'
59+--
60+-- All you need to do to use it is to use 'vanillaJSWith' and pass it @myOptions@.
61+--
62+-- @
63+-- jsCodeWithMyOptions :: String
64+-- jsCodeWithMyOptions = 'jsForAPI' api ('vanillaJSWith' myOptions)
65+-- @
66+--
67+-- Follow the same pattern for any other generator.
68+--
69+-- /Note/: The Angular generators take an additional type of options,
70+-- namely 'AngularOptions', to let you tweak aspects of the code generation
71+-- that are specific to /Angular.js/.
12module Servant.JS
72module Servant.JS
13− ( javascript
73+ ( -- * Generating javascript code from an API type
14− , generateJS
15− , jsForAPI
74+ jsForAPI
75+ , writeJSForAPI
76+ , JavaScriptGenerator
77+
78+ , -- * Options common to all generators
79+ CommonGeneratorOptions(..)
80+ , defCommonGeneratorOptions
81+
82+ , -- * Vanilla Javascript code generation
83+ vanillaJS
84+ , vanillaJSWith
85+
86+ , -- * JQuery code generation
87+ jquery
88+ , jqueryWith
89+
90+ , -- * Angular.js code generation
91+ angular
92+ , angularWith
93+ , angularService
94+ , angularServiceWith
95+ , AngularOptions(..)
96+ , defAngularOptions
97+
98+ , -- * Misc.
16− , listFromAPI
99+ listFromAPI
17− , printJS
100+ , javascript
18− , module Servant.JS.Internal
101+ , HasJS(..)
19− , GenerateCode(..)
102+ , GenerateList(..)
20− , CommonGeneratorOptions(..)
103+ , AjaxReq
21 ) where
104 ) where
22
105
23import Data.Proxy
106import Data.Proxy
24import Servant.API
107import Servant.API
108+import Servant.JS.Angular
25import Servant.JS.Internal
109import Servant.JS.Internal
110+import Servant.JS.JQuery
111+import Servant.JS.Vanilla
26
112
113+-- | Generate the data necessary to generate javascript code
114+-- for all the endpoints of an API, as ':<|>'-separated values
115+-- of type 'AjaxReq'.
27javascript :: HasJS layout => Proxy layout -> JS layout
116javascript :: HasJS layout => Proxy layout -> JS layout
28javascript p = javascriptFor p defReq
117javascript p = javascriptFor p defReq
29
118
30−printJS :: AjaxReq -> JavaScriptGenerator -> IO ()
31−printJS req gen = putStrLn (generateJS req gen)
32−
33−generateJS :: AjaxReq -> JavaScriptGenerator -> String
34−generateJS req gen = gen $ req
35−
36−-- | Utility class used by 'jsForAPI' which will
37−-- directly hand you all the Javascript code
38−-- instead of handing you a ':<|>'-separated list
39−-- of 'AjaxReq' like 'javascript' and then having to
40−-- use 'generateJS' on each 'AjaxReq'.
41−class GenerateCode reqs where
42− jsFor :: reqs -> JavaScriptGenerator -> String
43−
44−instance GenerateCode AjaxReq where
45− jsFor = generateJS
46−
47−instance GenerateCode rest => GenerateCode (AjaxReq :<|> rest) where
48− jsFor (req :<|> rest) gen = jsFor req gen ++ jsFor rest gen
49−
50-- | Directly generate all the javascript functions for your API
119-- | Directly generate all the javascript functions for your API
51-- from a 'Proxy' for your API type. You can then write it to
120-- from a 'Proxy' for your API type. You can then write it to
52-- a file or integrate it in a page, for example.
121-- a file or integrate it in a page, for example.
53−jsForAPI :: (HasJS api, GenerateCode (JS api)) => Proxy api
122+jsForAPI :: (HasJS api, GenerateList (JS api))
123+ => Proxy api -- ^ proxy for your API type
54− -> JavaScriptGenerator -> String
124+ -> JavaScriptGenerator -- ^ js code generator to use (angular, vanilla js, jquery, others)
55−jsForAPI p = jsFor (javascript p)
125+ -> String -- ^ a string that you can embed in your pages or write to a file
126+jsForAPI p gen = gen (listFromAPI p)
127+
128+-- | Directly generate all the javascript functions for your API
129+-- from a 'Proxy' for your API type using the given generator
130+-- and write the resulting code to a file at the given path.
131+writeJSForAPI :: (HasJS api, GenerateList (JS api))
132+ => Proxy api -- ^ proxy for your API type
133+ -> JavaScriptGenerator -- ^ js code generator to use (angular, vanilla js, jquery, others)
134+ -> FilePath -- ^ path to the file you want to write the resulting javascript code into
135+ -> IO ()
136+writeJSForAPI p gen fp = writeFile fp (jsForAPI p gen)
56
137
138+-- | Utility class used by 'jsForAPI' which computes
139+-- the data needed to generate a function for each endpoint
140+-- and hands it all back in a list.
57class GenerateList reqs where
141class GenerateList reqs where
58 generateList :: reqs -> [AjaxReq]
142 generateList :: reqs -> [AjaxReq]
59
143
@@ -63,5 +147,7 @@ instance GenerateList AjaxReq where
63instance GenerateList rest => GenerateList (AjaxReq :<|> rest) where
147instance GenerateList rest => GenerateList (AjaxReq :<|> rest) where
64 generateList (r :<|> rest) = r : generateList rest
148 generateList (r :<|> rest) = r : generateList rest
65
149
150+-- | Generate the necessary data for JS codegen as a list, each 'AjaxReq'
151+-- describing one endpoint from your API type.
66listFromAPI :: (HasJS api, GenerateList (JS api)) => Proxy api -> [AjaxReq]
152listFromAPI :: (HasJS api, GenerateList (JS api)) => Proxy api -> [AjaxReq]
67listFromAPI p = generateList (javascript p)
153listFromAPI p = generateList (javascript p)
servant-js/src/Servant/JS/Angular.hs+22−12
@@ -5,6 +5,7 @@ import Control.Lens
5import Data.List
5import Data.List
6import Data.Monoid
6import Data.Monoid
7
7
8+-- | Options specific to the angular code generator
8data AngularOptions = AngularOptions
9data AngularOptions = AngularOptions
9 { serviceName :: String -- ^ When generating code with wrapInService,
10 { serviceName :: String -- ^ When generating code with wrapInService,
10 -- name of the service to generate
11 -- name of the service to generate
@@ -24,28 +25,37 @@ defAngularOptions = AngularOptions
24-- | Instead of simply generating top level functions, generates a service instance
25-- | Instead of simply generating top level functions, generates a service instance
25-- on which your controllers can depend to access your API.
26-- on which your controllers can depend to access your API.
26-- This variant uses default 'AngularOptions'.
27-- This variant uses default 'AngularOptions'.
27−wrapInService :: AngularOptions -> [AjaxReq] -> String
28+angularService :: AngularOptions -> JavaScriptGenerator
28−wrapInService ngOpts reqs = wrapInServiceWith ngOpts defCommonGeneratorOptions reqs
29+angularService ngOpts = angularServiceWith ngOpts defCommonGeneratorOptions
29
30
30-- | Instead of simply generating top level functions, generates a service instance
31-- | Instead of simply generating top level functions, generates a service instance
31-- on which your controllers can depend to access your API
32-- on which your controllers can depend to access your API
32−wrapInServiceWith :: AngularOptions -> CommonGeneratorOptions -> [AjaxReq] -> String
33+angularServiceWith :: AngularOptions -> CommonGeneratorOptions -> JavaScriptGenerator
33−wrapInServiceWith ngOpts opts reqs =
34+angularServiceWith ngOpts opts reqs =
34− ((prologue ngOpts) svc mName)
35+ prologue ngOpts svc mName
35− <> (intercalate "," $ map generator reqs) <>
36+ <> intercalate "," (map generator reqs) <>
36− (epilogue ngOpts)
37+ epilogue ngOpts
37 where
38 where
38− generator req = (generateAngularJSWith ngOpts opts req)
39+ generator req = generateAngularJSWith ngOpts opts req
39 svc = serviceName ngOpts
40 svc = serviceName ngOpts
40 mName = if null (moduleName opts)
41 mName = if null (moduleName opts)
41 then "app."
42 then "app."
42− else (moduleName opts) <> "."
43+ else moduleName opts <> "."
43−
44+
45+-- | Generate regular javacript functions that use
46+-- the $http service, using default values for 'CommonGeneratorOptions'.
47+angular :: AngularOptions -> JavaScriptGenerator
48+angular ngopts = angularWith ngopts defCommonGeneratorOptions
49+
50+-- | Generate regular javascript functions that use the $http service.
51+angularWith :: AngularOptions -> CommonGeneratorOptions -> JavaScriptGenerator
52+angularWith ngopts opts = intercalate "\n\n" . map (generateAngularJSWith ngopts opts)
53+
44−-- js codegen using $http service from Angular using default options
54+-- | js codegen using $http service from Angular using default options
45generateAngularJS :: AngularOptions -> AjaxReq -> String
55generateAngularJS :: AngularOptions -> AjaxReq -> String
46generateAngularJS ngOpts = generateAngularJSWith ngOpts defCommonGeneratorOptions
56generateAngularJS ngOpts = generateAngularJSWith ngOpts defCommonGeneratorOptions
47
57
48−-- js codegen using $http service from Angular
58+-- | js codegen using $http service from Angular
49generateAngularJSWith :: AngularOptions -> CommonGeneratorOptions -> AjaxReq -> String
59generateAngularJSWith :: AngularOptions -> CommonGeneratorOptions -> AjaxReq -> String
50generateAngularJSWith ngOptions opts req = "\n" <>
60generateAngularJSWith ngOptions opts req = "\n" <>
51 fname <> fsep <> " function(" <> argsStr <> ")\n"
61 fname <> fsep <> " function(" <> argsStr <> ")\n"
servant-js/src/Servant/JS/Internal.hs+15−1
@@ -36,6 +36,17 @@ data CommonGeneratorOptions = CommonGeneratorOptions
36 , moduleName :: String -- ^ namespace on which we define the js function (empty mean local var)
36 , moduleName :: String -- ^ namespace on which we define the js function (empty mean local var)
37 }
37 }
38
38
39+-- | Default options.
40+--
41+-- @
42+-- > defCommonGeneratorOptions = CommonGeneratorOptions
43+-- > { functionRenamer = id
44+-- > , requestBody = "body"
45+-- > , successCallback = "onSuccess"
46+-- > , errorCallback = "onError"
47+-- > , moduleName = ""
48+-- > }
49+-- @
39defCommonGeneratorOptions :: CommonGeneratorOptions
50defCommonGeneratorOptions :: CommonGeneratorOptions
40defCommonGeneratorOptions = CommonGeneratorOptions
51defCommonGeneratorOptions = CommonGeneratorOptions
41 {
52 {
@@ -48,7 +59,10 @@ defCommonGeneratorOptions = CommonGeneratorOptions
48
59
49type Arg = String
60type Arg = String
50
61
62+-- A 'JavascriptGenerator' just takes the data found in the API type
63+-- for each endpoint and generates Javascript code in a String. Several
64+-- generators are available in this package.
51−type JavaScriptGenerator = AjaxReq -> String
65+type JavaScriptGenerator = [AjaxReq] -> String
52
66
53data Segment = Segment { _segment :: SegmentType, _matrix :: [MatrixArg] }
67data Segment = Segment { _segment :: SegmentType, _matrix :: [MatrixArg] }
54 deriving (Eq, Show)
68 deriving (Eq, Show)
servant-js/src/Servant/JS/JQuery.hs+11
@@ -5,6 +5,17 @@ import Control.Lens
5import Data.List
5import Data.List
6import Data.Monoid
6import Data.Monoid
7
7
8+-- | Generate javascript functions that use the /jQuery/ library
9+-- to make the AJAX calls. Uses 'defCommonGeneratorOptions'
10+-- for the generator options.
11+jquery :: JavaScriptGenerator
12+jquery = concat . map generateJQueryJS
13+
14+-- | Generate javascript functions that use the /jQuery/ library
15+-- to make the AJAX calls. Lets you specify your own 'CommonGeneratorOptions'.
16+jqueryWith :: CommonGeneratorOptions -> JavaScriptGenerator
17+jqueryWith opts = concat . map (generateJQueryJSWith opts)
18+
8-- | js codegen using JQuery using default options
19-- | js codegen using JQuery using default options
9generateJQueryJS :: AjaxReq -> String
20generateJQueryJS :: AjaxReq -> String
10generateJQueryJS = generateJQueryJSWith defCommonGeneratorOptions
21generateJQueryJS = generateJQueryJSWith defCommonGeneratorOptions
servant-js/src/Servant/JS/Vanilla.hs+12
@@ -5,6 +5,18 @@ import Control.Lens
5import Data.List
5import Data.List
6import Data.Monoid
6import Data.Monoid
7
7
8+-- | Generate vanilla javascript functions to make AJAX requests
9+-- to your API, using /XMLHttpRequest/. Uses 'defCommonGeneratorOptions'
10+-- for the 'CommonGeneratorOptions'.
11+vanillaJS :: JavaScriptGenerator
12+vanillaJS = concat . map generateVanillaJS
13+
14+-- | Generate vanilla javascript functions to make AJAX requests
15+-- to your API, using /XMLHttpRequest/. Lets you specify your
16+-- own options.
17+vanillaJSWith :: CommonGeneratorOptions -> JavaScriptGenerator
18+vanillaJSWith opts = concat . map (generateVanillaJSWith opts)
19+
8-- | js codegen using XmlHttpRequest using default generation options
20-- | js codegen using XmlHttpRequest using default generation options
9generateVanillaJS :: AjaxReq -> String
21generateVanillaJS :: AjaxReq -> String
10generateVanillaJS = generateVanillaJSWith defCommonGeneratorOptions
22generateVanillaJS = generateVanillaJSWith defCommonGeneratorOptions
servant-js/test/Servant/JSSpec.hs+5−7
@@ -97,15 +97,13 @@ angularSpec test = describe specLabel $ do
97 output _ = return ()
97 output _ = return ()
98 testName = "MyService"
98 testName = "MyService"
99 ngOpts = NG.defAngularOptions { NG.serviceName = testName }
99 ngOpts = NG.defAngularOptions { NG.serviceName = testName }
100− genJS req = NG.wrapInService ngOpts req
100+ genJS req = NG.angularService ngOpts req
101
101
102generateJSSpec :: TestNames -> (AjaxReq -> String) -> Spec
102generateJSSpec :: TestNames -> (AjaxReq -> String) -> Spec
103generateJSSpec n gen = describe specLabel $ do
103generateJSSpec n gen = describe specLabel $ do
104 it "should generate valid javascript" $ do
104 it "should generate valid javascript" $ do
105− let (postSimple :<|> getHasExtension ) = javascript (Proxy :: Proxy TestAPI)
105+ let s = jsForAPI (Proxy :: Proxy TestAPI) (concat . map gen)
106− parseFromString (genJS postSimple) `shouldSatisfy` isRight
107− parseFromString (genJS getHasExtension) `shouldSatisfy` isRight
106+ parseFromString s `shouldSatisfy` isRight
108− output $ genJS getHasExtension
109
107
110 it "should use non-empty function names" $ do
108 it "should use non-empty function names" $ do
111 let (_ :<|> topLevel) = javascript (Proxy :: Proxy TopLevelRawAPI)
109 let (_ :<|> topLevel) = javascript (Proxy :: Proxy TopLevelRawAPI)
@@ -141,13 +139,13 @@ generateJSSpec n gen = describe specLabel $ do
141 jsText `shouldContain` (header n "X-WhatsForDinner" $ "\"I would like \" + headerXWhatsForDinner + \" with a cherry on top.\"")
139 jsText `shouldContain` (header n "X-WhatsForDinner" $ "\"I would like \" + headerXWhatsForDinner + \" with a cherry on top.\"")
142
140
143 it "can generate the whole javascript code string at once with jsForAPI" $ do
141 it "can generate the whole javascript code string at once with jsForAPI" $ do
144− let jsStr = jsForAPI (Proxy :: Proxy TestAPI) gen
142+ let jsStr = jsForAPI (Proxy :: Proxy TestAPI) (concat . map gen)
145 parseFromString jsStr `shouldSatisfy` isRight
143 parseFromString jsStr `shouldSatisfy` isRight
146 where
144 where
147 specLabel = "generateJS(" ++ (show n) ++ ")"
145 specLabel = "generateJS(" ++ (show n) ++ ")"
148 --output = print
146 --output = print
149 output _ = return ()
147 output _ = return ()
150− genJS req = generateJS req gen
148+ genJS req = gen req
151 header :: TestNames -> String -> String -> String
149 header :: TestNames -> String -> String -> String
152 header v headerName headerValue
150 header v headerName headerValue
153 | v `elem` [Vanilla, VanillaCustom] = "xhr.setRequestHeader(\"" ++ headerName ++ "\", " ++ headerValue ++ ");\n"
151 | v `elem` [Vanilla, VanillaCustom] = "xhr.setRequestHeader(\"" ++ headerName ++ "\", " ++ headerValue ++ ");\n"
servant-js/test/Servant/JSSpec/CustomHeaders.hs+1
@@ -14,6 +14,7 @@ import Data.Proxy
14import GHC.TypeLits
14import GHC.TypeLits
15import Servant.API
15import Servant.API
16import Servant.JS
16import Servant.JS
17+import Servant.JS.Internal
17
18
18-- | This is a hypothetical combinator that fetches an Authorization header.
19-- | This is a hypothetical combinator that fetches an Authorization header.
19-- The symbol in the header denotes what kind of authentication we are
20-- The symbol in the header denotes what kind of authentication we are