Verb for -mock, -js and -foreign.

This commit is contained in:
Julian K. Arni 2016-01-06 18:20:20 +01:00
parent 208bcf5986
commit bd77b4acba
8 changed files with 38 additions and 74 deletions

View file

@ -27,10 +27,11 @@ source-repository head
library library
exposed-modules: Servant.Foreign, Servant.Foreign.Internal exposed-modules: Servant.Foreign, Servant.Foreign.Internal
build-depends: base == 4.* build-depends: base == 4.*
, lens == 4.* , lens == 4.*
, servant == 0.5.* , servant == 0.5.*
, text >= 1.2 && < 1.3 , text >= 1.2 && < 1.3
, http-types
hs-source-dirs: src hs-source-dirs: src
default-language: Haskell2010 default-language: Haskell2010
ghc-options: -Wall ghc-options: -Wall
@ -41,6 +42,7 @@ test-suite spec
type: exitcode-stdio-1.0 type: exitcode-stdio-1.0
hs-source-dirs: test hs-source-dirs: test
ghc-options: -Wall ghc-options: -Wall
include-dirs: include
main-is: Spec.hs main-is: Spec.hs
other-modules: other-modules:
Servant.ForeignSpec Servant.ForeignSpec

View file

@ -13,18 +13,21 @@
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
-- | Generalizes all the data needed to make code generation work with -- | Generalizes all the data needed to make code generation work with
-- arbitrary programming languages. -- arbitrary programming languages.
module Servant.Foreign.Internal where module Servant.Foreign.Internal where
import Control.Lens (makeLenses, (%~), (&), (.~), (<>~)) import Control.Lens (makeLenses, (%~), (&), (.~), (<>~))
import qualified Data.Char as C import qualified Data.Char as C
import Data.Proxy import Data.Proxy
import Data.Text import Data.Text
import GHC.Exts (Constraint) import Data.Text.Encoding (decodeUtf8)
import GHC.Exts (Constraint)
import GHC.TypeLits import GHC.TypeLits
import Prelude hiding (concat) import qualified Network.HTTP.Types as HTTP
import Prelude hiding (concat)
import Servant.API import Servant.API
-- | Function name builder that simply concat each part together -- | Function name builder that simply concat each part together
@ -86,11 +89,10 @@ defUrl :: Url
defUrl = Url [] [] defUrl = Url [] []
type FunctionName = [Text] type FunctionName = [Text]
type Method = Text
data Req = Req data Req = Req
{ _reqUrl :: Url { _reqUrl :: Url
, _reqMethod :: Method , _reqMethod :: HTTP.Method
, _reqHeaders :: [HeaderArg] , _reqHeaders :: [HeaderArg]
, _reqBody :: Maybe ForeignType , _reqBody :: Maybe ForeignType
, _reqReturnType :: ForeignType , _reqReturnType :: ForeignType
@ -185,27 +187,18 @@ instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
str = pack . symbolVal $ (Proxy :: Proxy sym) str = pack . symbolVal $ (Proxy :: Proxy sym)
arg = (str, typeFor lang (Proxy :: Proxy a)) arg = (str, typeFor lang (Proxy :: Proxy a))
instance (Elem JSON list, HasForeignType lang a) instance (Elem JSON list, HasForeignType lang a, ReflectMethod method)
=> HasForeign lang (Delete list a) where => HasForeign lang (Verb method status list a) where
type Foreign (Delete list a) = Req type Foreign (Verb method status list a) = Req
foreignFor lang Proxy req = foreignFor lang Proxy req =
req & funcName %~ ("delete" :) req & funcName %~ (methodLC :)
& reqMethod .~ "DELETE" & reqMethod .~ method
& reqReturnType .~ retType & reqReturnType .~ retType
where where
retType = typeFor lang (Proxy :: Proxy a) retType = typeFor lang (Proxy :: Proxy a)
method = reflectMethod (Proxy :: Proxy method)
instance (Elem JSON list, HasForeignType lang a) methodLC = toLower $ decodeUtf8 method
=> HasForeign lang (Get list a) where
type Foreign (Get list a) = Req
foreignFor lang Proxy req =
req & funcName %~ ("get" :)
& reqMethod .~ "GET"
& reqReturnType .~ retType
where
retType = typeFor lang (Proxy :: Proxy a)
instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout) instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
=> HasForeign lang (Header sym a :> sublayout) where => HasForeign lang (Header sym a :> sublayout) where
@ -220,28 +213,6 @@ instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
arg = (hname, typeFor lang (Proxy :: Proxy a)) arg = (hname, typeFor lang (Proxy :: Proxy a))
subP = Proxy :: Proxy sublayout subP = Proxy :: Proxy sublayout
instance (Elem JSON list, HasForeignType lang a)
=> HasForeign lang (Post list a) where
type Foreign (Post list a) = Req
foreignFor lang Proxy req =
req & funcName %~ ("post" :)
& reqMethod .~ "POST"
& reqReturnType .~ retType
where
retType = typeFor lang (Proxy :: Proxy a)
instance (Elem JSON list, HasForeignType lang a)
=> HasForeign lang (Put list a) where
type Foreign (Put list a) = Req
foreignFor lang Proxy req =
req & funcName %~ ("put" :)
& reqMethod .~ "PUT"
& reqReturnType .~ retType
where
retType = typeFor lang (Proxy :: Proxy a)
instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout) instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
=> HasForeign lang (QueryParam sym a :> sublayout) where => HasForeign lang (QueryParam sym a :> sublayout) where
type Foreign (QueryParam sym a :> sublayout) = Foreign sublayout type Foreign (QueryParam sym a :> sublayout) = Foreign sublayout
@ -279,10 +250,10 @@ instance (KnownSymbol sym, HasForeignType lang a, a ~ Bool, HasForeign lang subl
arg = (str, typeFor lang (Proxy :: Proxy a)) arg = (str, typeFor lang (Proxy :: Proxy a))
instance HasForeign lang Raw where instance HasForeign lang Raw where
type Foreign Raw = Method -> Req type Foreign Raw = HTTP.Method -> Req
foreignFor _ Proxy req method = foreignFor _ Proxy req method =
req & funcName %~ ((toLower method) :) req & funcName %~ ((toLower $ decodeUtf8 method) :)
& reqMethod .~ method & reqMethod .~ method
instance (Elem JSON list, HasForeignType lang a, HasForeign lang sublayout) instance (Elem JSON list, HasForeignType lang a, HasForeign lang sublayout)
@ -346,4 +317,3 @@ instance (GenerateList start, GenerateList rest) => GenerateList (start :<|> res
-- describing one endpoint from your API type. -- describing one endpoint from your API type.
listFromAPI :: (HasForeign lang api, GenerateList (Foreign api)) => Proxy lang -> Proxy api -> [Req] listFromAPI :: (HasForeign lang api, GenerateList (Foreign api)) => Proxy lang -> Proxy api -> [Req]
listFromAPI lang p = generateList (foreignFor lang p defReq) listFromAPI lang p = generateList (foreignFor lang p defReq)

View file

@ -7,9 +7,8 @@
{-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
#if __GLASGOW_HASKELL__ < 710
{-# LANGUAGE OverlappingInstances #-} #include "overlapping-compat.h"
#endif
module Servant.ForeignSpec where module Servant.ForeignSpec where
@ -41,9 +40,9 @@ instance HasForeignType LangX Int where
typeFor _ _ = "intX" typeFor _ _ = "intX"
instance HasForeignType LangX Bool where instance HasForeignType LangX Bool where
typeFor _ _ = "boolX" typeFor _ _ = "boolX"
instance {-# Overlapping #-} HasForeignType LangX String where instance OVERLAPPING_ HasForeignType LangX String where
typeFor _ _ = "stringX" typeFor _ _ = "stringX"
instance {-# Overlappable #-} HasForeignType LangX a => HasForeignType LangX [a] where instance OVERLAPPABLE_ HasForeignType LangX a => HasForeignType LangX [a] where
typeFor lang _ = "listX of " <> typeFor lang (Proxy :: Proxy a) typeFor lang _ = "listX of " <> typeFor lang (Proxy :: Proxy a)
type TestApi type TestApi

View file

@ -6,6 +6,7 @@ import Data.Maybe (isJust)
import Data.Monoid import Data.Monoid
import qualified Data.Text as T import qualified Data.Text as T
import Data.Text (Text) import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8)
import Servant.Foreign import Servant.Foreign
import Servant.JS.Internal import Servant.JS.Internal
@ -68,7 +69,7 @@ generateAngularJSWith ngOptions opts req = "\n" <>
<> " { url: " <> url <> "\n" <> " { url: " <> url <> "\n"
<> dataBody <> dataBody
<> reqheaders <> reqheaders
<> " , method: '" <> method <> "'\n" <> " , method: '" <> decodeUtf8 method <> "'\n"
<> " });\n" <> " });\n"
<> "}\n" <> "}\n"

View file

@ -5,6 +5,7 @@ import Control.Lens
import Data.Maybe (isJust) import Data.Maybe (isJust)
import Data.Monoid import Data.Monoid
import Data.Text (Text) import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8)
import qualified Data.Text as T import qualified Data.Text as T
import Servant.Foreign import Servant.Foreign
import Servant.JS.Internal import Servant.JS.Internal
@ -117,7 +118,7 @@ generateAxiosJSWith aopts opts req = "\n" <>
fname = namespace <> (functionNameBuilder opts $ req ^. funcName) fname = namespace <> (functionNameBuilder opts $ req ^. funcName)
method = T.toLower $ req ^. reqMethod method = T.toLower . decodeUtf8 $ req ^. reqMethod
url = if url' == "'" then "'/'" else url' url = if url' == "'" then "'/'" else url'
url' = "'" url' = "'"
<> urlPrefix opts <> urlPrefix opts

View file

@ -6,6 +6,7 @@ import Data.Maybe (isJust)
import Data.Monoid import Data.Monoid
import qualified Data.Text as T import qualified Data.Text as T
import Data.Text (Text) import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8)
import Servant.Foreign import Servant.Foreign
import Servant.JS.Internal import Servant.JS.Internal
@ -35,7 +36,7 @@ generateJQueryJSWith opts req = "\n" <>
<> dataBody <> dataBody
<> reqheaders <> reqheaders
<> " , error: " <> onError <> "\n" <> " , error: " <> onError <> "\n"
<> " , type: '" <> method <> "'\n" <> " , type: '" <> decodeUtf8 method <> "'\n"
<> " });\n" <> " });\n"
<> "}\n" <> "}\n"

View file

@ -4,6 +4,7 @@ module Servant.JS.Vanilla where
import Control.Lens import Control.Lens
import Data.Maybe (isJust) import Data.Maybe (isJust)
import Data.Text (Text) import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8)
import qualified Data.Text as T import qualified Data.Text as T
import Data.Monoid import Data.Monoid
import Servant.Foreign import Servant.Foreign
@ -31,7 +32,7 @@ generateVanillaJSWith opts req = "\n" <>
fname <> " = function(" <> argsStr <> ")\n" fname <> " = function(" <> argsStr <> ")\n"
<> "{\n" <> "{\n"
<> " var xhr = new XMLHttpRequest();\n" <> " var xhr = new XMLHttpRequest();\n"
<> " xhr.open('" <> method <> "', " <> url <> ", true);\n" <> " xhr.open('" <> decodeUtf8 method <> "', " <> url <> ", true);\n"
<> reqheaders <> reqheaders
<> " xhr.setRequestHeader(\"Accept\",\"application/json\");\n" <> " xhr.setRequestHeader(\"Accept\",\"application/json\");\n"
<> (if isJust (req ^. reqBody) then " xhr.setRequestHeader(\"Content-Type\",\"application/json\");\n" else "") <> (if isJust (req ^. reqBody) then " xhr.setRequestHeader(\"Content-Type\",\"application/json\");\n" else "")

View file

@ -139,19 +139,8 @@ instance (KnownSymbol s, HasMock rest) => HasMock (QueryFlag s :> rest) where
instance (KnownSymbol h, FromHttpApiData a, HasMock rest) => HasMock (Header h a :> rest) where instance (KnownSymbol h, FromHttpApiData a, HasMock rest) => HasMock (Header h a :> rest) where
mock _ = \_ -> mock (Proxy :: Proxy rest) mock _ = \_ -> mock (Proxy :: Proxy rest)
instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Delete ctypes a) where instance (Arbitrary a, KnownNat status, ReflectMethod method, AllCTRender ctypes a)
mock _ = mockArbitrary => HasMock (Verb method status ctypes a) where
instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Get ctypes a) where
mock _ = mockArbitrary
instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Patch ctypes a) where
mock _ = mockArbitrary
instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Post ctypes a) where
mock _ = mockArbitrary
instance (Arbitrary a, AllCTRender ctypes a) => HasMock (Put ctypes a) where
mock _ = mockArbitrary mock _ = mockArbitrary
instance HasMock Raw where instance HasMock Raw where