servant-foreign (empty) → 0.5
raw patch · 11 files changed
+702/−0 lines, 11 filesdep +basedep +hspecdep +http-typessetup-changed
Dependencies added: base, hspec, http-types, lens, servant, servant-foreign, text
Files
- CHANGELOG.md +5/−0
- LICENSE +30/−0
- README.md +5/−0
- Setup.hs +2/−0
- include/overlapping-compat.h +8/−0
- servant-foreign.cabal +80/−0
- src/Servant/Foreign.hs +56/−0
- src/Servant/Foreign/Inflections.hs +45/−0
- src/Servant/Foreign/Internal.hs +362/−0
- test/Servant/ForeignSpec.hs +108/−0
- test/Spec.hs +1/−0
+ CHANGELOG.md view
@@ -0,0 +1,5 @@+0.5+-----+* Use the `text` package instead of `String`.+* Extract javascript-oblivious types and helpers to *servant-foreign*+* Typed-languages support
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014-2016, Zalora South East Asia Pte Ltd, Servant Contributors++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Zalora South East Asia Pte Ltd nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,5 @@+# servant-foreign++++Types and helpers for generating clients for *servant* servers in arbitrary programming languages.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ include/overlapping-compat.h view
@@ -0,0 +1,8 @@+#if __GLASGOW_HASKELL__ >= 710+#define OVERLAPPABLE_ {-# OVERLAPPABLE #-}+#define OVERLAPPING_ {-# OVERLAPPING #-}+#else+{-# LANGUAGE OverlappingInstances #-}+#define OVERLAPPABLE_+#define OVERLAPPING_+#endif
+ servant-foreign.cabal view
@@ -0,0 +1,80 @@+name: servant-foreign+version: 0.5+synopsis: Helpers for generating clients for servant APIs in any programming language+description:+ Helper types and functions for generating client functions for servant APIs in any programming language+ .+ This package provides types and functions that collect all the data needed to generate client functions in the programming language of your choice. This effectively means you only have to write the code that "pretty-prints" this data as some code in your target language.+ .+ See the servant-js package for an example+ .+ <https://github.com/haskell-servant/servant/blob/master/servant-foreign/CHANGELOG.md CHANGELOG>+license: BSD3+license-file: LICENSE+author: Servant Contributors+maintainer: haskell-servant-maintainers@googlegroups.com+copyright: 2015-2016 Servant Contributors+category: Web+build-type: Simple+cabal-version: >=1.10+extra-source-files:+ include/*.h+ CHANGELOG.md+ README.md+source-repository head+ type: git+ location: http://github.com/haskell-servant/servant.git++library+ exposed-modules: Servant.Foreign+ , Servant.Foreign.Internal+ , Servant.Foreign.Inflections+ build-depends: base == 4.*+ , lens == 4.*+ , servant == 0.5.*+ , text >= 1.2 && < 1.3+ , http-types+ hs-source-dirs: src+ default-language: Haskell2010+ ghc-options: -Wall+ include-dirs: include+ default-extensions: CPP+ , ConstraintKinds+ , DataKinds+ , FlexibleContexts+ , FlexibleInstances+ , GeneralizedNewtypeDeriving+ , MultiParamTypeClasses+ , ScopedTypeVariables+ , StandaloneDeriving+ , TemplateHaskell+ , TypeFamilies+ , TypeOperators+ , UndecidableInstances+ , OverloadedStrings+ , PolyKinds+++test-suite spec+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ ghc-options: -Wall+ include-dirs: include+ main-is: Spec.hs+ other-modules: Servant.ForeignSpec+ build-depends: base+ , hspec >= 2.1.8+ , servant-foreign+ default-language: Haskell2010+ default-extensions: ConstraintKinds+ , DataKinds+ , FlexibleContexts+ , FlexibleInstances+ , GeneralizedNewtypeDeriving+ , MultiParamTypeClasses+ , ScopedTypeVariables+ , TypeFamilies+ , TypeOperators+ , UndecidableInstances+ , OverloadedStrings+ , PolyKinds
+ src/Servant/Foreign.hs view
@@ -0,0 +1,56 @@+-- | Generalizes all the data needed to make code generation work with+-- arbitrary programming languages.+module Servant.Foreign+ ( ArgType(..)+ , HeaderArg(..)+ , QueryArg(..)+ , Req(..)+ , Segment(..)+ , SegmentType(..)+ , Url(..)+ -- aliases+ , Path+ , Arg(..)+ , FunctionName(..)+ , PathSegment(..)+ -- lenses+ , argName+ , argType+ , argPath+ , reqUrl+ , reqMethod+ , reqHeaders+ , reqBody+ , reqReturnType+ , reqFuncName+ , path+ , queryStr+ , queryArgName+ , queryArgType+ , headerArg+ -- prisms+ , _PathSegment+ , _HeaderArg+ , _ReplaceHeaderArg+ , _Static+ , _Cap+ , _Normal+ , _Flag+ , _List+ -- rest of it+ , HasForeign(..)+ , HasForeignType(..)+ , GenerateList(..)+ , NoTypes+ , captureArg+ , isCapture+ , defReq+ , listFromAPI+ -- re-exports+ , module Servant.API+ , module Servant.Foreign.Inflections+ ) where++import Servant.API+import Servant.Foreign.Internal+import Servant.Foreign.Inflections
+ src/Servant/Foreign/Inflections.hs view
@@ -0,0 +1,45 @@+module Servant.Foreign.Inflections+ ( concatCase+ , snakeCase+ , camelCase+ -- lenses+ , concatCaseL+ , snakeCaseL+ , camelCaseL+ ) where+++import Control.Lens hiding (cons)+import qualified Data.Char as C+import Data.Monoid+import Data.Text hiding (map)+import Prelude hiding (head, tail)+import Servant.Foreign.Internal++concatCaseL :: Getter FunctionName Text+concatCaseL = _FunctionName . to mconcat++-- | Function name builder that simply concat each part together+concatCase :: FunctionName -> Text+concatCase = view concatCaseL++snakeCaseL :: Getter FunctionName Text+snakeCaseL = _FunctionName . to (intercalate "_")++-- | Function name builder using the snake_case convention.+-- each part is separated by a single underscore character.+snakeCase :: FunctionName -> Text+snakeCase = view snakeCaseL++camelCaseL :: Getter FunctionName Text+camelCaseL = _FunctionName . to (convert . map (replace "-" ""))+ where+ convert [] = ""+ convert (p:ps) = mconcat $ p : map capitalize ps+ capitalize "" = ""+ capitalize name = C.toUpper (head name) `cons` tail name++-- | Function name builder using the CamelCase convention.+-- each part begins with an upper case character.+camelCase :: FunctionName -> Text+camelCase = view camelCaseL
+ src/Servant/Foreign/Internal.hs view
@@ -0,0 +1,362 @@+{-# LANGUAGE CPP #-}+#if !MIN_VERSION_base(4,8,0)+{-# LANGUAGE NullaryTypeClasses #-}+#endif++-- | Generalizes all the data needed to make code generation work with+-- arbitrary programming languages.+module Servant.Foreign.Internal where++import Control.Lens hiding (cons, List)+#if !MIN_VERSION_base(4,8,0)+import Data.Monoid+#endif+import Data.Proxy+import Data.String+import Data.Text+import Data.Text.Encoding (decodeUtf8)+import GHC.Exts (Constraint)+import GHC.TypeLits+import qualified Network.HTTP.Types as HTTP+import Prelude hiding (concat)+import Servant.API+++newtype FunctionName = FunctionName { unFunctionName :: [Text] }+ deriving (Show, Eq, Monoid)++makePrisms ''FunctionName++newtype PathSegment = PathSegment { unPathSegment :: Text }+ deriving (Show, Eq, IsString, Monoid)++makePrisms ''PathSegment++data Arg f = Arg+ { _argName :: PathSegment+ , _argType :: f }++deriving instance Eq f => Eq (Arg f)+deriving instance Show f => Show (Arg f)++makeLenses ''Arg++argPath :: Getter (Arg f) Text+argPath = argName . _PathSegment++data SegmentType f+ = Static PathSegment+ -- ^ a static path segment. like "/foo"+ | Cap (Arg f)+ -- ^ a capture. like "/:userid"++deriving instance Eq f => Eq (SegmentType f)+deriving instance Show f => Show (SegmentType f)++makePrisms ''SegmentType++newtype Segment f = Segment { unSegment :: SegmentType f }++deriving instance Eq f => Eq (Segment f)+deriving instance Show f => Show (Segment f)++makePrisms ''Segment++isCapture :: Segment f -> Bool+isCapture (Segment (Cap _)) = True+isCapture _ = False++captureArg :: Segment f -> Arg f+captureArg (Segment (Cap s)) = s+captureArg _ = error "captureArg called on non capture"++type Path f = [Segment f]++data ArgType+ = Normal+ | Flag+ | List+ deriving (Eq, Show)++makePrisms ''ArgType++data QueryArg f = QueryArg+ { _queryArgName :: Arg f+ , _queryArgType :: ArgType+ }++deriving instance Eq f => Eq (QueryArg f)+deriving instance Show f => Show (QueryArg f)++makeLenses ''QueryArg++data HeaderArg f = HeaderArg+ { _headerArg :: Arg f }+ | ReplaceHeaderArg+ { _headerArg :: Arg f+ , _headerPattern :: Text+ }++deriving instance Eq f => Eq (HeaderArg f)+deriving instance Show f => Show (HeaderArg f)++makeLenses ''HeaderArg++makePrisms ''HeaderArg++data Url f = Url+ { _path :: Path f+ , _queryStr :: [QueryArg f]+ }++deriving instance Eq f => Eq (Url f)+deriving instance Show f => Show (Url f)++defUrl :: Url f+defUrl = Url [] []++makeLenses ''Url++data Req f = Req+ { _reqUrl :: Url f+ , _reqMethod :: HTTP.Method+ , _reqHeaders :: [HeaderArg f]+ , _reqBody :: Maybe f+ , _reqReturnType :: Maybe f+ , _reqFuncName :: FunctionName+ }++deriving instance Eq f => Eq (Req f)+deriving instance Show f => Show (Req f)++makeLenses ''Req++defReq :: Req ftype+defReq = Req defUrl "GET" [] Nothing Nothing (FunctionName [])++-- | To be used exclusively as a "negative" return type/constraint+-- by @'Elem`@ type family.+class NotFound++type family Elem (a :: *) (ls::[*]) :: Constraint where+ Elem a '[] = NotFound+ Elem a (a ': list) = ()+ Elem a (b ': list) = Elem a list++-- | 'HasForeignType' maps Haskell types with types in the target+-- language of your backend. For example, let's say you're+-- implementing a backend to some language __X__, and you want+-- a Text representation of each input/output type mentioned in the API:+--+-- > -- First you need to create a dummy type to parametrize your+-- > -- instances.+-- > data LangX+-- >+-- > -- Otherwise you define instances for the types you need+-- > instance HasForeignType LangX Text Int where+-- > typeFor _ _ _ = "intX"+-- >+-- > -- Or for example in case of lists+-- > instance HasForeignType LangX Text a => HasForeignType LangX Text [a] where+-- > typeFor lang type _ = "listX of " <> typeFor lang ftype (Proxy :: Proxy a)+--+-- Finally to generate list of information about all the endpoints for+-- an API you create a function of a form:+--+-- > getEndpoints :: (HasForeign LangX Text api, GenerateList Text (Foreign Text api))+-- > => Proxy api -> [Req Text]+-- > getEndpoints api = listFromAPI (Proxy :: Proxy LangX) (Proxy :: Proxy Text) api+--+-- > -- If language __X__ is dynamically typed then you can use+-- > -- a predefined NoTypes parameter with the () output type:+--+-- > getEndpoints :: (HasForeign NoTypes () api, GenerateList Text (Foreign () api))+-- > => Proxy api -> [Req ()]+-- > getEndpoints api = listFromAPI (Proxy :: Proxy NoTypes) (Proxy :: Proxy ()) api+-- >+--+class HasForeignType lang ftype a where+ typeFor :: Proxy lang -> Proxy ftype -> Proxy a -> ftype++data NoTypes++instance HasForeignType NoTypes () ftype where+ typeFor _ _ _ = ()++class HasForeign lang ftype (layout :: *) where+ type Foreign ftype layout :: *+ foreignFor :: Proxy lang -> Proxy ftype -> Proxy layout -> Req ftype -> Foreign ftype layout++instance (HasForeign lang ftype a, HasForeign lang ftype b)+ => HasForeign lang ftype (a :<|> b) where+ type Foreign ftype (a :<|> b) = Foreign ftype a :<|> Foreign ftype b++ foreignFor lang ftype Proxy req =+ foreignFor lang ftype (Proxy :: Proxy a) req+ :<|> foreignFor lang ftype (Proxy :: Proxy b) req++instance (KnownSymbol sym, HasForeignType lang ftype t, HasForeign lang ftype sublayout)+ => HasForeign lang ftype (Capture sym t :> sublayout) where+ type Foreign ftype (Capture sym a :> sublayout) = Foreign ftype sublayout++ foreignFor lang Proxy Proxy req =+ foreignFor lang Proxy (Proxy :: Proxy sublayout) $+ req & reqUrl . path <>~ [Segment (Cap arg)]+ & reqFuncName . _FunctionName %~ (++ ["by", str])+ where+ str = pack . symbolVal $ (Proxy :: Proxy sym)+ ftype = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy t)+ arg = Arg+ { _argName = PathSegment str+ , _argType = ftype }++instance (Elem JSON list, HasForeignType lang ftype a, ReflectMethod method)+ => HasForeign lang ftype (Verb method status list a) where+ type Foreign ftype (Verb method status list a) = Req ftype++ foreignFor lang Proxy Proxy req =+ req & reqFuncName . _FunctionName %~ (methodLC :)+ & reqMethod .~ method+ & reqReturnType .~ Just retType+ where+ retType = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy a)+ method = reflectMethod (Proxy :: Proxy method)+ methodLC = toLower $ decodeUtf8 method++instance (KnownSymbol sym, HasForeignType lang ftype a, HasForeign lang ftype sublayout)+ => HasForeign lang ftype (Header sym a :> sublayout) where+ type Foreign ftype (Header sym a :> sublayout) = Foreign ftype sublayout++ foreignFor lang Proxy Proxy req =+ foreignFor lang Proxy subP $ req & reqHeaders <>~ [HeaderArg arg]+ where+ hname = pack . symbolVal $ (Proxy :: Proxy sym)+ arg = Arg+ { _argName = PathSegment hname+ , _argType = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy a) }+ subP = Proxy :: Proxy sublayout++instance (KnownSymbol sym, HasForeignType lang ftype a, HasForeign lang ftype sublayout)+ => HasForeign lang ftype (QueryParam sym a :> sublayout) where+ type Foreign ftype (QueryParam sym a :> sublayout) = Foreign ftype sublayout++ foreignFor lang Proxy Proxy req =+ foreignFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy sublayout) $+ req & reqUrl.queryStr <>~ [QueryArg arg Normal]+ where+ str = pack . symbolVal $ (Proxy :: Proxy sym)+ arg = Arg+ { _argName = PathSegment str+ , _argType = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy a) }++instance+ (KnownSymbol sym, HasForeignType lang ftype [a], HasForeign lang ftype sublayout)+ => HasForeign lang ftype (QueryParams sym a :> sublayout) where+ type Foreign ftype (QueryParams sym a :> sublayout) = Foreign ftype sublayout+ foreignFor lang Proxy Proxy req =+ foreignFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy sublayout) $+ req & reqUrl.queryStr <>~ [QueryArg arg List]+ where+ str = pack . symbolVal $ (Proxy :: Proxy sym)+ arg = Arg+ { _argName = PathSegment str+ , _argType = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy [a]) }++instance+ (KnownSymbol sym, HasForeignType lang ftype Bool, HasForeign lang ftype sublayout)+ => HasForeign lang ftype (QueryFlag sym :> sublayout) where+ type Foreign ftype (QueryFlag sym :> sublayout) = Foreign ftype sublayout++ foreignFor lang ftype Proxy req =+ foreignFor lang ftype (Proxy :: Proxy sublayout) $+ req & reqUrl.queryStr <>~ [QueryArg arg Flag]+ where+ str = pack . symbolVal $ (Proxy :: Proxy sym)+ arg = Arg+ { _argName = PathSegment str+ , _argType = typeFor lang ftype (Proxy :: Proxy Bool) }++instance HasForeign lang ftype Raw where+ type Foreign ftype Raw = HTTP.Method -> Req ftype++ foreignFor _ Proxy Proxy req method =+ req & reqFuncName . _FunctionName %~ ((toLower $ decodeUtf8 method) :)+ & reqMethod .~ method++instance (Elem JSON list, HasForeignType lang ftype a, HasForeign lang ftype sublayout)+ => HasForeign lang ftype (ReqBody list a :> sublayout) where+ type Foreign ftype (ReqBody list a :> sublayout) = Foreign ftype sublayout++ foreignFor lang ftype Proxy req =+ foreignFor lang ftype (Proxy :: Proxy sublayout) $+ req & reqBody .~ (Just $ typeFor lang ftype (Proxy :: Proxy a))++instance (KnownSymbol path, HasForeign lang ftype sublayout)+ => HasForeign lang ftype (path :> sublayout) where+ type Foreign ftype (path :> sublayout) = Foreign ftype sublayout++ foreignFor lang ftype Proxy req =+ foreignFor lang ftype (Proxy :: Proxy sublayout) $+ req & reqUrl . path <>~ [Segment (Static (PathSegment str))]+ & reqFuncName . _FunctionName %~ (++ [str])+ where+ str =+ Data.Text.map (\c -> if c == '.' then '_' else c)+ . pack . symbolVal $ (Proxy :: Proxy path)++instance HasForeign lang ftype sublayout+ => HasForeign lang ftype (RemoteHost :> sublayout) where+ type Foreign ftype (RemoteHost :> sublayout) = Foreign ftype sublayout++ foreignFor lang ftype Proxy req =+ foreignFor lang ftype (Proxy :: Proxy sublayout) req++instance HasForeign lang ftype sublayout+ => HasForeign lang ftype (IsSecure :> sublayout) where+ type Foreign ftype (IsSecure :> sublayout) = Foreign ftype sublayout++ foreignFor lang ftype Proxy req =+ foreignFor lang ftype (Proxy :: Proxy sublayout) req++instance HasForeign lang ftype sublayout => HasForeign lang ftype (Vault :> sublayout) where+ type Foreign ftype (Vault :> sublayout) = Foreign ftype sublayout++ foreignFor lang ftype Proxy req =+ foreignFor lang ftype (Proxy :: Proxy sublayout) req++instance HasForeign lang ftype sublayout =>+ HasForeign lang ftype (WithNamedContext name context sublayout) where++ type Foreign ftype (WithNamedContext name context sublayout) = Foreign ftype sublayout++ foreignFor lang ftype Proxy = foreignFor lang ftype (Proxy :: Proxy sublayout)++instance HasForeign lang ftype sublayout+ => HasForeign lang ftype (HttpVersion :> sublayout) where+ type Foreign ftype (HttpVersion :> sublayout) = Foreign ftype sublayout++ foreignFor lang ftype Proxy req =+ foreignFor lang ftype (Proxy :: Proxy sublayout) req++-- | Utility class used by 'listFromAPI' which computes+-- the data needed to generate a function for each endpoint+-- and hands it all back in a list.+class GenerateList ftype reqs where+ generateList :: reqs -> [Req ftype]++instance GenerateList ftype (Req ftype) where+ generateList r = [r]++instance (GenerateList ftype start, GenerateList ftype rest)+ => GenerateList ftype (start :<|> rest) where+ generateList (start :<|> rest) = (generateList start) ++ (generateList rest)++-- | Generate the necessary data for codegen as a list, each 'Req'+-- describing one endpoint from your API type.+listFromAPI+ :: (HasForeign lang ftype api, GenerateList ftype (Foreign ftype api))+ => Proxy lang+ -> Proxy ftype+ -> Proxy api+ -> [Req ftype]+listFromAPI lang ftype p = generateList (foreignFor lang ftype p defReq)
+ test/Servant/ForeignSpec.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE CPP #-}+#include "overlapping-compat.h"++module Servant.ForeignSpec where++import Data.Monoid ((<>))+import Data.Proxy+import Servant.Foreign++import Test.Hspec++spec :: Spec+spec = describe "Servant.Foreign" $ do+ camelCaseSpec+ listFromAPISpec++camelCaseSpec :: Spec+camelCaseSpec = describe "camelCase" $ do+ it "converts FunctionNames to camelCase" $ do+ camelCase (FunctionName ["post", "counter", "inc"])+ `shouldBe` "postCounterInc"+ camelCase (FunctionName ["get", "hyphen-ated", "counter"])+ `shouldBe` "getHyphenatedCounter"++----------------------------------------------------------------------++data LangX++instance HasForeignType LangX String () where+ typeFor _ _ _ = "voidX"++instance HasForeignType LangX String Int where+ typeFor _ _ _ = "intX"++instance HasForeignType LangX String Bool where+ typeFor _ _ _ = "boolX"++instance OVERLAPPING_ HasForeignType LangX String String where+ typeFor _ _ _ = "stringX"++instance OVERLAPPABLE_ HasForeignType LangX String a => HasForeignType LangX String [a] where+ typeFor lang ftype _ = "listX of " <> typeFor lang ftype (Proxy :: Proxy a)++type TestApi+ = "test" :> Header "header" [String] :> QueryFlag "flag" :> Get '[JSON] Int+ :<|> "test" :> QueryParam "param" Int :> ReqBody '[JSON] [String] :> Post '[JSON] ()+ :<|> "test" :> QueryParams "params" Int :> ReqBody '[JSON] String :> Put '[JSON] ()+ :<|> "test" :> Capture "id" Int :> Delete '[JSON] ()++testApi :: [Req String]+testApi = listFromAPI (Proxy :: Proxy LangX) (Proxy :: Proxy String) (Proxy :: Proxy TestApi)++listFromAPISpec :: Spec+listFromAPISpec = describe "listFromAPI" $ do+ it "generates 4 endpoints for TestApi" $ do+ length testApi `shouldBe` 4++ let [getReq, postReq, putReq, deleteReq] = testApi++ it "collects all info for get request" $ do+ shouldBe getReq $ defReq+ { _reqUrl = Url+ [ Segment $ Static "test" ]+ [ QueryArg (Arg "flag" "boolX") Flag ]+ , _reqMethod = "GET"+ , _reqHeaders = [HeaderArg $ Arg "header" "listX of stringX"]+ , _reqBody = Nothing+ , _reqReturnType = Just "intX"+ , _reqFuncName = FunctionName ["get", "test"]+ }++ it "collects all info for post request" $ do+ shouldBe postReq $ defReq+ { _reqUrl = Url+ [ Segment $ Static "test" ]+ [ QueryArg (Arg "param" "intX") Normal ]+ , _reqMethod = "POST"+ , _reqHeaders = []+ , _reqBody = Just "listX of stringX"+ , _reqReturnType = Just "voidX"+ , _reqFuncName = FunctionName ["post", "test"]+ }++ it "collects all info for put request" $ do+ shouldBe putReq $ defReq+ { _reqUrl = Url+ [ Segment $ Static "test" ]+ -- Shoud this be |intX| or |listX of intX| ?+ [ QueryArg (Arg "params" "listX of intX") List ]+ , _reqMethod = "PUT"+ , _reqHeaders = []+ , _reqBody = Just "stringX"+ , _reqReturnType = Just "voidX"+ , _reqFuncName = FunctionName ["put", "test"]+ }++ it "collects all info for delete request" $ do+ shouldBe deleteReq $ defReq+ { _reqUrl = Url+ [ Segment $ Static "test"+ , Segment $ Cap (Arg "id" "intX") ]+ []+ , _reqMethod = "DELETE"+ , _reqHeaders = []+ , _reqBody = Nothing+ , _reqReturnType = Just "voidX"+ , _reqFuncName = FunctionName ["delete", "test", "by", "id"]+ }
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}