on master help sign in
Extended HeaderArg to allow header arguments to replace the contents of a template, allowing specially composed headers to create specially formatted headers
e16f90fGeoffrey Roberts
servant-jquery.cabal+2−1
@@ -31,7 +31,7 @@ flag example
31library
31library
32 exposed-modules: Servant.JQuery
32 exposed-modules: Servant.JQuery
33 other-modules: Servant.JQuery.Internal
33 other-modules: Servant.JQuery.Internal
34− build-depends: base >=4.5 && <5, servant >= 0.2.1, lens >= 4
34+ build-depends: base >=4.5 && <5, servant >= 0.2.1, lens >= 4, MissingH
35 hs-source-dirs: src
35 hs-source-dirs: src
36 default-language: Haskell2010
36 default-language: Haskell2010
37 ghc-options: -Wall
37 ghc-options: -Wall
@@ -68,5 +68,6 @@ test-suite spec
68 , servant-jquery
68 , servant-jquery
69 , servant
69 , servant
70 , hspec >= 2.0
70 , hspec >= 2.0
71+ , hspec-expectations
71 , language-ecmascript == 0.16.*
72 , language-ecmascript == 0.16.*
72 default-language: Haskell2010
73 default-language: Haskell2010
src/Servant/JQuery.hs+3−3
@@ -44,7 +44,7 @@ generateJS req = "\n" <>
44 args = captures
44 args = captures
45 ++ map (view argName) queryparams
45 ++ map (view argName) queryparams
46 ++ body
46 ++ body
47− ++ map ("header"++) hs
47+ ++ map ((<>) "header" . headerArgName) hs
48 ++ ["onSuccess", "onError"]
48 ++ ["onSuccess", "onError"]
49
49
50 captures = map captureArg
50 captures = map captureArg
@@ -67,10 +67,10 @@ generateJS req = "\n" <>
67 reqheaders =
67 reqheaders =
68 if null hs
68 if null hs
69 then ""
69 then ""
70− else "\n , headers: { " ++ headersStr ++ " } }\n"
70+ else "\n , headers: { " ++ headersStr ++ " }\n"
71
71
72 where headersStr = intercalate ", " $ map headerStr hs
72 where headersStr = intercalate ", " $ map headerStr hs
73− headerStr hname = "\"" ++ hname ++ "\": header" ++ hname
73+ headerStr header = "\"" ++ headerArgName header ++ "\": " ++ show header
74
74
75 fname = req ^. funcName
75 fname = req ^. funcName
76 method = req ^. reqMethod
76 method = req ^. reqMethod
src/Servant/JQuery/Internal.hs+22−2
@@ -9,8 +9,10 @@ module Servant.JQuery.Internal where
9import Control.Applicative
9import Control.Applicative
10import Control.Lens
10import Control.Lens
11import Data.Char (toLower)
11import Data.Char (toLower)
12+import Data.List
12import Data.Monoid
13import Data.Monoid
13import Data.Proxy
14import Data.Proxy
15+import Data.String.Utils
14import GHC.TypeLits
16import GHC.TypeLits
15import Servant.API
17import Servant.API
16
18
@@ -53,7 +55,25 @@ data QueryArg = QueryArg
53 , _argType :: ArgType
55 , _argType :: ArgType
54 } deriving (Eq, Show)
56 } deriving (Eq, Show)
55
57
56−type HeaderArg = String
58+data HeaderArg = HeaderArg
59+ { headerArgName :: String
60+ }
61+ | ReplaceHeaderArg
62+ { headerArgName :: String
63+ , headerPattern :: String
64+ } deriving (Eq)
65+
66+instance Show HeaderArg where
67+ show (HeaderArg n) = "header" <> n
68+ show (ReplaceHeaderArg n p)
69+ | pn `startswith` p = pv <> " + \"" <> rp <> "\""
70+ | pn `endswith` p = "\"" <> rp <> "\" + " <> pv
71+ | pn `isInfixOf` p = "\"" <> replace pn ("\"" <> pv <> "\"") p <> "\""
72+ | otherwise = p
73+ where
74+ pv = "header" <> n
75+ pn = "{header" <> n <> "}"
76+ rp = replace pn "" p
57
77
58data Url = Url
78data Url = Url
59 { _path :: Path
79 { _path :: Path
@@ -144,7 +164,7 @@ instance (KnownSymbol sym, HasJQ sublayout)
144 type JQ (Header sym a :> sublayout) = JQ sublayout
164 type JQ (Header sym a :> sublayout) = JQ sublayout
145
165
146 jqueryFor Proxy req =
166 jqueryFor Proxy req =
147− jqueryFor subP (req & reqHeaders <>~ [hname])
167+ jqueryFor subP (req & reqHeaders <>~ [HeaderArg hname])
148
168
149 where hname = symbolVal (Proxy :: Proxy sym)
169 where hname = symbolVal (Proxy :: Proxy sym)
150 subP = Proxy :: Proxy sublayout
170 subP = Proxy :: Proxy sublayout
test/Servant/JQuerySpec.hs+19−3
@@ -1,6 +1,9 @@
1−{-# LANGUAGE DataKinds #-}
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE FlexibleInstances #-}
2−{-# LANGUAGE QuasiQuotes #-}
3+{-# LANGUAGE QuasiQuotes #-}
4+{-# LANGUAGE ScopedTypeVariables #-}
5+{-# LANGUAGE TypeFamilies #-}
3−{-# LANGUAGE TypeOperators #-}
6+{-# LANGUAGE TypeOperators #-}
4{-# OPTIONS_GHC -fno-warn-orphans #-}
7{-# OPTIONS_GHC -fno-warn-orphans #-}
5module Servant.JQuerySpec where
8module Servant.JQuerySpec where
6
9
@@ -20,6 +23,12 @@ GET /has.extension Bool
20type TopLevelRawAPI = "something" :> Get Int
23type TopLevelRawAPI = "something" :> Get Int
21 :<|> Raw
24 :<|> Raw
22
25
26+type HeaderHandlingAPI = "something" :> Header "Foo" String
27+ :> Get Int
28+
29+headerHandlingProxy :: Proxy HeaderHandlingAPI
30+headerHandlingProxy = Proxy
31+
23spec :: Spec
32spec :: Spec
24spec = describe "Servant.JQuery"
33spec = describe "Servant.JQuery"
25 generateJSSpec
34 generateJSSpec
@@ -37,3 +46,10 @@ generateJSSpec = describe "generateJS" $ do
37 print $ generateJS $ topLevel "GET"
46 print $ generateJS $ topLevel "GET"
38 parseFromString (generateJS $ topLevel "GET") `shouldSatisfy` isRight
47 parseFromString (generateJS $ topLevel "GET") `shouldSatisfy` isRight
39
48
49+ it "should handle simple HTTP headers" $ do
50+ let jsText = generateJS $ jquery headerHandlingProxy
51+ print jsText
52+ parseFromString jsText `shouldSatisfy` isRight
53+ jsText `shouldContain` "headerFoo"
54+ jsText `shouldContain` "headers: { \"Foo\": headerFoo }\n"
55+