Merge commit '8ecc3f07064a3a627b8e49fb182789c82cd9c5d7' into jkarni/config

This commit is contained in:
aaron levin 2016-01-16 17:46:23 +01:00
commit 2e7778d1d6
20 changed files with 409 additions and 145 deletions

View file

@ -25,7 +25,7 @@ install:
- sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config - sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
script: script:
- for package in $(cat sources.txt); do (cd $package && tinc && cabal configure --enable-tests --disable-optimization && cabal build && cabal test) || exit 1; done - for package in $(cat sources.txt); do (echo testing $package && cd $package && tinc && cabal configure --enable-tests --disable-optimization && cabal build && cabal test) || exit 1; done
cache: cache:
directories: directories:

View file

@ -77,3 +77,10 @@ the `news` label if you make a new package so we can know about it!
We are currently moving to a more aggresive release policy, so that you can get We are currently moving to a more aggresive release policy, so that you can get
what you contribute from Hackage fairly soon. However, note that prior to major what you contribute from Hackage fairly soon. However, note that prior to major
releases it may take some time in between releases. releases it may take some time in between releases.
## Reporting security issues
Please email haskell-servant-maintainers AT googlegroups DOT com. This group is
private, and accessible only to known maintainers. We will then discuss how to
proceed. Please do not make the issue public before we inform you that we have
a patch ready.

View file

@ -1,4 +1,5 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}

View file

@ -735,6 +735,15 @@ instance OVERLAPPING_
status = fromInteger $ natVal (Proxy :: Proxy status) status = fromInteger $ natVal (Proxy :: Proxy status)
p = Proxy :: Proxy a p = Proxy :: Proxy a
instance (KnownSymbol sym, HasDocs sublayout)
=> HasDocs (Header sym a :> sublayout) where
docsFor Proxy (endpoint, action) =
docsFor sublayoutP (endpoint, action')
where sublayoutP = Proxy :: Proxy sublayout
action' = over headers (|> headername) action
headername = T.pack $ symbolVal (Proxy :: Proxy sym)
instance (KnownSymbol sym, ToParam (QueryParam sym a), HasDocs sublayout) instance (KnownSymbol sym, ToParam (QueryParam sym a), HasDocs sublayout)
=> HasDocs (QueryParam sym a :> sublayout) where => HasDocs (QueryParam sym a :> sublayout) where

View file

@ -63,6 +63,7 @@ spec = describe "Servant.Docs" $ do
, ("zwei, kaks, kaks",(TT2,UT2,UT2)) , ("zwei, kaks, kaks",(TT2,UT2,UT2))
] ]
where where
tests md = do tests md = do
it "mentions supported content-types" $ do it "mentions supported content-types" $ do
@ -76,11 +77,15 @@ spec = describe "Servant.Docs" $ do
md `shouldContain` "POST" md `shouldContain` "POST"
md `shouldContain` "GET" md `shouldContain` "GET"
it "mentions headers" $ do
md `shouldContain` "- This endpoint is sensitive to the value of the **X-Test** HTTP header."
it "contains response samples" $ it "contains response samples" $
md `shouldContain` "{\"dt1field1\":\"field 1\",\"dt1field2\":13}" md `shouldContain` "{\"dt1field1\":\"field 1\",\"dt1field2\":13}"
it "contains request body samples" $ it "contains request body samples" $
md `shouldContain` "17" md `shouldContain` "17"
-- * APIs -- * APIs
data Datatype1 = Datatype1 { dt1field1 :: String data Datatype1 = Datatype1 { dt1field1 :: String
@ -103,6 +108,7 @@ instance MimeRender PlainText Int where
type TestApi1 = Get '[JSON, PlainText] (Headers '[Header "Location" String] Int) type TestApi1 = Get '[JSON, PlainText] (Headers '[Header "Location" String] Int)
:<|> ReqBody '[JSON] String :> Post '[JSON] Datatype1 :<|> ReqBody '[JSON] String :> Post '[JSON] Datatype1
:<|> Header "X-Test" Int :> Put '[JSON] Int
data TT = TT1 | TT2 deriving (Show, Eq) data TT = TT1 | TT2 deriving (Show, Eq)
data UT = UT1 | UT2 deriving (Show, Eq) data UT = UT1 | UT2 deriving (Show, Eq)

View file

@ -20,4 +20,4 @@ api :: Proxy API
api = Proxy api = Proxy
main :: IO () main :: IO ()
main = run 8080 (serve api $ mock api) main = run 8080 (serve api EmptyConfig $ mock api)

View file

@ -18,8 +18,7 @@ cabal-version: >=1.10
flag example flag example
description: Build the example too description: Build the example too
manual: True default: True
default: False
library library
exposed-modules: exposed-modules:

View file

@ -3,6 +3,7 @@
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-orphans #-}

View file

@ -99,11 +99,13 @@ test-suite spec
hs-source-dirs: test hs-source-dirs: test
main-is: Spec.hs main-is: Spec.hs
other-modules: other-modules:
Servant.Server.Internal.EnterSpec
Servant.Server.Internal.ConfigSpec
Servant.ServerSpec
Servant.Utils.StaticFilesSpec
Servant.Server.ErrorSpec Servant.Server.ErrorSpec
Servant.Server.Internal.ConfigSpec
Servant.Server.Internal.EnterSpec
Servant.ServerSpec
Servant.Server.UsingConfigSpec
Servant.Server.UsingConfigSpec.TestCombinators
Servant.Utils.StaticFilesSpec
build-depends: build-depends:
base == 4.* base == 4.*
, aeson , aeson

View file

@ -1,3 +1,4 @@
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilies #-}
@ -35,11 +36,6 @@ module Servant.Server
, generalizeNat , generalizeNat
, tweakResponse , tweakResponse
-- * Config
, ConfigEntry(..)
, Config(..)
, (.:.)
-- * General Authentication -- * General Authentication
, AuthHandler(unAuthHandler) , AuthHandler(unAuthHandler)
, AuthReturnType , AuthReturnType
@ -49,6 +45,10 @@ module Servant.Server
, BasicAuthCheck(BasicAuthCheck, unBasicAuthCheck) , BasicAuthCheck(BasicAuthCheck, unBasicAuthCheck)
, BasicAuthResult(..) , BasicAuthResult(..)
-- * Config
, Config(..)
, NamedConfig(..)
-- * Default error type -- * Default error type
, ServantErr(..) , ServantErr(..)
-- ** 3XX -- ** 3XX
@ -77,7 +77,7 @@ module Servant.Server
, err415 , err415
, err416 , err416
, err417 , err417
-- * 5XX -- ** 5XX
, err500 , err500
, err501 , err501
, err502 , err502
@ -110,18 +110,18 @@ import Servant.Server.Internal.Enter
-- > myApi :: Proxy MyApi -- > myApi :: Proxy MyApi
-- > myApi = Proxy -- > myApi = Proxy
-- > -- >
-- > cfg :: Config '[] -- > config :: Config '[]
-- > cfg = EmptyConfig -- > config = EmptyConfig
-- > -- >
-- > app :: Application -- > app :: Application
-- > app = serve myApi cfg server -- > app = serve myApi config server
-- > -- >
-- > main :: IO () -- > main :: IO ()
-- > main = Network.Wai.Handler.Warp.run 8080 app -- > main = Network.Wai.Handler.Warp.run 8080 app
-- --
serve :: (HasCfg layout a, HasServer layout) serve :: (HasConfig layout a, HasServer layout)
=> Proxy layout -> Config a -> Server layout -> Application => Proxy layout -> Config a -> Server layout -> Application
serve p cfg server = toApplication (runRouter (route p cfg d)) serve p config server = toApplication (runRouter (route p config d))
where where
d = Delayed r r r r (\ _ _ _ -> Route server) d = Delayed r r r r (\ _ _ _ -> Route server)
r = return (Route ()) r = return (Route ())

View file

@ -1,4 +1,5 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
@ -53,7 +54,8 @@ import Servant.API ((:<|>) (..), (:>), AuthProtect, Ba
Verb, ReflectMethod(reflectMethod), Verb, ReflectMethod(reflectMethod),
IsSecure(..), Header, IsSecure(..), Header,
QueryFlag, QueryParam, QueryParams, QueryFlag, QueryParam, QueryParams,
Raw, RemoteHost, ReqBody, Vault) Raw, RemoteHost, ReqBody, Vault,
WithNamedConfig)
import Servant.API.ContentTypes (AcceptHeader (..), import Servant.API.ContentTypes (AcceptHeader (..),
AllCTRender (..), AllCTRender (..),
AllCTUnrender (..), AllCTUnrender (..),
@ -71,9 +73,9 @@ import Servant.Server.Internal.ServantErr
class HasServer layout where class HasServer layout where
type ServerT layout (m :: * -> *) :: * type ServerT layout (m :: * -> *) :: *
type HasCfg layout (c :: [*]) :: Constraint type HasConfig layout (c :: [*]) :: Constraint
route :: HasCfg layout a => Proxy layout -> Config a -> Delayed (Server layout) -> Router route :: HasConfig layout a => Proxy layout -> Config a -> Delayed (Server layout) -> Router
type Server layout = ServerT layout (ExceptT ServantErr IO) type Server layout = ServerT layout (ExceptT ServantErr IO)
@ -93,10 +95,10 @@ type Server layout = ServerT layout (ExceptT ServantErr IO)
instance (HasServer a, HasServer b) => HasServer (a :<|> b) where instance (HasServer a, HasServer b) => HasServer (a :<|> b) where
type ServerT (a :<|> b) m = ServerT a m :<|> ServerT b m type ServerT (a :<|> b) m = ServerT a m :<|> ServerT b m
type HasCfg (a :<|> b) c = (HasCfg a c, HasCfg b c) type HasConfig (a :<|> b) c = (HasConfig a c, HasConfig b c)
route Proxy cfg server = choice (route pa cfg ((\ (a :<|> _) -> a) <$> server)) route Proxy config server = choice (route pa config ((\ (a :<|> _) -> a) <$> server))
(route pb cfg ((\ (_ :<|> b) -> b) <$> server)) (route pb config ((\ (_ :<|> b) -> b) <$> server))
where pa = Proxy :: Proxy a where pa = Proxy :: Proxy a
pb = Proxy :: Proxy b pb = Proxy :: Proxy b
@ -125,12 +127,12 @@ instance (KnownSymbol capture, FromHttpApiData a, HasServer sublayout)
type ServerT (Capture capture a :> sublayout) m = type ServerT (Capture capture a :> sublayout) m =
a -> ServerT sublayout m a -> ServerT sublayout m
type HasCfg (Capture capture a :> sublayout) c = (HasCfg sublayout c) type HasConfig (Capture capture a :> sublayout) c = (HasConfig sublayout c)
route Proxy cfg d = route Proxy config d =
DynamicRouter $ \ first -> DynamicRouter $ \ first ->
route (Proxy :: Proxy sublayout) route (Proxy :: Proxy sublayout)
cfg config
(addCapture d $ case captured captureProxy first of (addCapture d $ case captured captureProxy first of
Nothing -> return $ Fail err404 Nothing -> return $ Fail err404
Just v -> return $ Route v Just v -> return $ Route v
@ -202,7 +204,7 @@ instance OVERLAPPABLE_
) => HasServer (Verb method status ctypes a) where ) => HasServer (Verb method status ctypes a) where
type ServerT (Verb method status ctypes a) m = m a type ServerT (Verb method status ctypes a) m = m a
type HasCfg (Verb method status ctypes a) c = () type HasConfig (Verb method status ctypes a) c = ()
route Proxy _ = methodRouter method (Proxy :: Proxy ctypes) status route Proxy _ = methodRouter method (Proxy :: Proxy ctypes) status
where method = reflectMethod (Proxy :: Proxy method) where method = reflectMethod (Proxy :: Proxy method)
@ -214,7 +216,7 @@ instance OVERLAPPING_
) => HasServer (Verb method status ctypes (Headers h a)) where ) => HasServer (Verb method status ctypes (Headers h a)) where
type ServerT (Verb method status ctypes (Headers h a)) m = m (Headers h a) type ServerT (Verb method status ctypes (Headers h a)) m = m (Headers h a)
type HasCfg (Verb method status ctypes (Headers h a)) c = () type HasConfig (Verb method status ctypes (Headers h a)) c = ()
route Proxy _ = methodRouterHeaders method (Proxy :: Proxy ctypes) status route Proxy _ = methodRouterHeaders method (Proxy :: Proxy ctypes) status
where method = reflectMethod (Proxy :: Proxy method) where method = reflectMethod (Proxy :: Proxy method)
@ -245,11 +247,11 @@ instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
type ServerT (Header sym a :> sublayout) m = type ServerT (Header sym a :> sublayout) m =
Maybe a -> ServerT sublayout m Maybe a -> ServerT sublayout m
type HasCfg (Header sym a :> sublayout) c = HasCfg sublayout c type HasConfig (Header sym a :> sublayout) c = HasConfig sublayout c
route Proxy cfg subserver = WithRequest $ \ request -> route Proxy config subserver = WithRequest $ \ request ->
let mheader = parseHeaderMaybe =<< lookup str (requestHeaders request) let mheader = parseHeaderMaybe =<< lookup str (requestHeaders request)
in route (Proxy :: Proxy sublayout) cfg (passToServer subserver mheader) in route (Proxy :: Proxy sublayout) config (passToServer subserver mheader)
where str = fromString $ symbolVal (Proxy :: Proxy sym) where str = fromString $ symbolVal (Proxy :: Proxy sym)
-- | If you use @'QueryParam' "author" Text@ in one of the endpoints for your API, -- | If you use @'QueryParam' "author" Text@ in one of the endpoints for your API,
@ -278,9 +280,9 @@ instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
type ServerT (QueryParam sym a :> sublayout) m = type ServerT (QueryParam sym a :> sublayout) m =
Maybe a -> ServerT sublayout m Maybe a -> ServerT sublayout m
type HasCfg (QueryParam sym a :> sublayout) c = HasCfg sublayout c type HasConfig (QueryParam sym a :> sublayout) c = HasConfig sublayout c
route Proxy cfg subserver = WithRequest $ \ request -> route Proxy config subserver = WithRequest $ \ request ->
let querytext = parseQueryText $ rawQueryString request let querytext = parseQueryText $ rawQueryString request
param = param =
case lookup paramname querytext of case lookup paramname querytext of
@ -288,7 +290,7 @@ instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
Just Nothing -> Nothing -- param present with no value -> Nothing Just Nothing -> Nothing -- param present with no value -> Nothing
Just (Just v) -> parseQueryParamMaybe v -- if present, we try to convert to Just (Just v) -> parseQueryParamMaybe v -- if present, we try to convert to
-- the right type -- the right type
in route (Proxy :: Proxy sublayout) cfg (passToServer subserver param) in route (Proxy :: Proxy sublayout) config (passToServer subserver param)
where paramname = cs $ symbolVal (Proxy :: Proxy sym) where paramname = cs $ symbolVal (Proxy :: Proxy sym)
-- | If you use @'QueryParams' "authors" Text@ in one of the endpoints for your API, -- | If you use @'QueryParams' "authors" Text@ in one of the endpoints for your API,
@ -315,16 +317,16 @@ instance (KnownSymbol sym, FromHttpApiData a, HasServer sublayout)
type ServerT (QueryParams sym a :> sublayout) m = type ServerT (QueryParams sym a :> sublayout) m =
[a] -> ServerT sublayout m [a] -> ServerT sublayout m
type HasCfg (QueryParams sym a :> sublayout) c = HasCfg sublayout c type HasConfig (QueryParams sym a :> sublayout) c = HasConfig sublayout c
route Proxy cfg subserver = WithRequest $ \ request -> route Proxy config subserver = WithRequest $ \ request ->
let querytext = parseQueryText $ rawQueryString request let querytext = parseQueryText $ rawQueryString request
-- if sym is "foo", we look for query string parameters -- if sym is "foo", we look for query string parameters
-- named "foo" or "foo[]" and call parseQueryParam on the -- named "foo" or "foo[]" and call parseQueryParam on the
-- corresponding values -- corresponding values
parameters = filter looksLikeParam querytext parameters = filter looksLikeParam querytext
values = mapMaybe (convert . snd) parameters values = mapMaybe (convert . snd) parameters
in route (Proxy :: Proxy sublayout) cfg (passToServer subserver values) in route (Proxy :: Proxy sublayout) config (passToServer subserver values)
where paramname = cs $ symbolVal (Proxy :: Proxy sym) where paramname = cs $ symbolVal (Proxy :: Proxy sym)
looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]") looksLikeParam (name, _) = name == paramname || name == (paramname <> "[]")
convert Nothing = Nothing convert Nothing = Nothing
@ -347,15 +349,15 @@ instance (KnownSymbol sym, HasServer sublayout)
type ServerT (QueryFlag sym :> sublayout) m = type ServerT (QueryFlag sym :> sublayout) m =
Bool -> ServerT sublayout m Bool -> ServerT sublayout m
type HasCfg (QueryFlag sym :> sublayout) c = HasCfg sublayout c type HasConfig (QueryFlag sym :> sublayout) c = HasConfig sublayout c
route Proxy cfg subserver = WithRequest $ \ request -> route Proxy config subserver = WithRequest $ \ request ->
let querytext = parseQueryText $ rawQueryString request let querytext = parseQueryText $ rawQueryString request
param = case lookup paramname querytext of param = case lookup paramname querytext of
Just Nothing -> True -- param is there, with no value Just Nothing -> True -- param is there, with no value
Just (Just v) -> examine v -- param with a value Just (Just v) -> examine v -- param with a value
Nothing -> False -- param not in the query string Nothing -> False -- param not in the query string
in route (Proxy :: Proxy sublayout) cfg (passToServer subserver param) in route (Proxy :: Proxy sublayout) config (passToServer subserver param)
where paramname = cs $ symbolVal (Proxy :: Proxy sym) where paramname = cs $ symbolVal (Proxy :: Proxy sym)
examine v | v == "true" || v == "1" || v == "" = True examine v | v == "true" || v == "1" || v == "" = True
| otherwise = False | otherwise = False
@ -371,7 +373,7 @@ instance (KnownSymbol sym, HasServer sublayout)
instance HasServer Raw where instance HasServer Raw where
type ServerT Raw m = Application type ServerT Raw m = Application
type HasCfg Raw c = () type HasConfig Raw c = ()
route Proxy _ rawApplication = LeafRouter $ \ request respond -> do route Proxy _ rawApplication = LeafRouter $ \ request respond -> do
r <- runDelayed rawApplication r <- runDelayed rawApplication
@ -406,10 +408,10 @@ instance ( AllCTUnrender list a, HasServer sublayout
type ServerT (ReqBody list a :> sublayout) m = type ServerT (ReqBody list a :> sublayout) m =
a -> ServerT sublayout m a -> ServerT sublayout m
type HasCfg (ReqBody list a :> sublayout) c = HasCfg sublayout c type HasConfig (ReqBody list a :> sublayout) c = HasConfig sublayout c
route Proxy cfg subserver = WithRequest $ \ request -> route Proxy config subserver = WithRequest $ \ request ->
route (Proxy :: Proxy sublayout) cfg (addBodyCheck subserver (bodyCheck request)) route (Proxy :: Proxy sublayout) config (addBodyCheck subserver (bodyCheck request))
where where
bodyCheck request = do bodyCheck request = do
-- See HTTP RFC 2616, section 7.2.1 -- See HTTP RFC 2616, section 7.2.1
@ -430,67 +432,67 @@ instance ( AllCTUnrender list a, HasServer sublayout
instance (KnownSymbol path, HasServer sublayout) => HasServer (path :> sublayout) where instance (KnownSymbol path, HasServer sublayout) => HasServer (path :> sublayout) where
type ServerT (path :> sublayout) m = ServerT sublayout m type ServerT (path :> sublayout) m = ServerT sublayout m
type HasCfg (path :> sublayout) c = HasCfg sublayout c type HasConfig (path :> sublayout) c = HasConfig sublayout c
route Proxy cfg subserver = StaticRouter $ route Proxy config subserver = StaticRouter $
M.singleton (cs (symbolVal proxyPath)) M.singleton (cs (symbolVal proxyPath))
(route (Proxy :: Proxy sublayout) cfg subserver) (route (Proxy :: Proxy sublayout) config subserver)
where proxyPath = Proxy :: Proxy path where proxyPath = Proxy :: Proxy path
instance HasServer api => HasServer (RemoteHost :> api) where instance HasServer api => HasServer (RemoteHost :> api) where
type ServerT (RemoteHost :> api) m = SockAddr -> ServerT api m type ServerT (RemoteHost :> api) m = SockAddr -> ServerT api m
type HasCfg (RemoteHost :> api) c = HasCfg api c type HasConfig (RemoteHost :> api) c = HasConfig api c
route Proxy cfg subserver = WithRequest $ \req -> route Proxy config subserver = WithRequest $ \req ->
route (Proxy :: Proxy api) cfg (passToServer subserver $ remoteHost req) route (Proxy :: Proxy api) config (passToServer subserver $ remoteHost req)
instance HasServer api => HasServer (IsSecure :> api) where instance HasServer api => HasServer (IsSecure :> api) where
type ServerT (IsSecure :> api) m = IsSecure -> ServerT api m type ServerT (IsSecure :> api) m = IsSecure -> ServerT api m
type HasCfg (IsSecure :> api) c = HasCfg api c type HasConfig (IsSecure :> api) c = HasConfig api c
route Proxy cfg subserver = WithRequest $ \req -> route Proxy config subserver = WithRequest $ \req ->
route (Proxy :: Proxy api) cfg (passToServer subserver $ secure req) route (Proxy :: Proxy api) config (passToServer subserver $ secure req)
where secure req = if isSecure req then Secure else NotSecure where secure req = if isSecure req then Secure else NotSecure
instance HasServer api => HasServer (Vault :> api) where instance HasServer api => HasServer (Vault :> api) where
type ServerT (Vault :> api) m = Vault -> ServerT api m type ServerT (Vault :> api) m = Vault -> ServerT api m
type HasCfg (Vault :> api) c = HasCfg api c type HasConfig (Vault :> api) c = HasConfig api c
route Proxy cfg subserver = WithRequest $ \req -> route Proxy config subserver = WithRequest $ \req ->
route (Proxy :: Proxy api) cfg (passToServer subserver $ vault req) route (Proxy :: Proxy api) config (passToServer subserver $ vault req)
instance HasServer api => HasServer (HttpVersion :> api) where instance HasServer api => HasServer (HttpVersion :> api) where
type ServerT (HttpVersion :> api) m = HttpVersion -> ServerT api m type ServerT (HttpVersion :> api) m = HttpVersion -> ServerT api m
type HasCfg (HttpVersion :> api) c = HasCfg api c type HasConfig (HttpVersion :> api) c = HasConfig api c
route Proxy cfg subserver = WithRequest $ \req -> route Proxy config subserver = WithRequest $ \req ->
route (Proxy :: Proxy api) cfg (passToServer subserver $ httpVersion req) route (Proxy :: Proxy api) config (passToServer subserver $ httpVersion req)
-- | Basic Authentication -- | Basic Authentication
instance (KnownSymbol realm, HasServer api) instance (KnownSymbol realm, HasServer api)
=> HasServer (BasicAuth tag realm usr :> api) where => HasServer (BasicAuth tag realm usr :> api) where
type ServerT (BasicAuth tag realm usr :> api) m = usr -> ServerT api m type ServerT (BasicAuth tag realm usr :> api) m = usr -> ServerT api m
type HasCfg (BasicAuth tag realm usr :> api) c type HasConfig (BasicAuth tag realm usr :> api) c
= (HasConfigEntry c tag (BasicAuthCheck usr), HasCfg api c) = (HasConfigEntry c (BasicAuthCheck usr), HasConfig api c)
route Proxy cfg subserver = WithRequest $ \ request -> route Proxy config subserver = WithRequest $ \ request ->
route (Proxy :: Proxy api) cfg (subserver `addAuthCheck` authCheck request) route (Proxy :: Proxy api) config (subserver `addAuthCheck` authCheck request)
where where
realm = BC8.pack $ symbolVal (Proxy :: Proxy realm) realm = BC8.pack $ symbolVal (Proxy :: Proxy realm)
baCfg = getConfigEntry (Proxy :: Proxy tag) cfg basicAuthConfig = getConfigEntry config
authCheck req = runBasicAuth req realm baCfg authCheck req = runBasicAuth req realm basicAuthConfig
-- | General Authentication -- | General Authentication
instance HasServer api => HasServer (AuthProtect tag :> api) where instance HasServer api => HasServer (AuthProtect tag :> api) where
type ServerT (AuthProtect tag :> api) m = AuthReturnType (AuthProtect tag) -> ServerT api m type ServerT (AuthProtect tag :> api) m = AuthReturnType (AuthProtect tag) -> ServerT api m
type HasCfg (AuthProtect tag :> api) c type HasConfig (AuthProtect tag :> api) c
= (HasConfigEntry c tag (AuthHandler Request (AuthReturnType (AuthProtect tag))), HasCfg api c) = (HasConfigEntry c (AuthHandler Request (AuthReturnType (AuthProtect tag))), HasConfig api c)
route Proxy cfg subserver = WithRequest $ \ request -> route Proxy config subserver = WithRequest $ \ request ->
route (Proxy :: Proxy api) cfg (subserver `addAuthCheck` authCheck request) route (Proxy :: Proxy api) config (subserver `addAuthCheck` authCheck request)
where where
authHandler = unAuthHandler (getConfigEntry (Proxy :: Proxy tag) cfg) authHandler = unAuthHandler (getConfigEntry config)
authCheck = fmap (either FailFatal Route) . runExceptT . authHandler authCheck = fmap (either FailFatal Route) . runExceptT . authHandler
pathIsEmpty :: Request -> Bool pathIsEmpty :: Request -> Bool
@ -501,3 +503,20 @@ pathIsEmpty = go . pathInfo
ct_wildcard :: B.ByteString ct_wildcard :: B.ByteString
ct_wildcard = "*" <> "/" <> "*" -- Because CPP ct_wildcard = "*" <> "/" <> "*" -- Because CPP
-- * configs
instance HasServer subApi => HasServer (WithNamedConfig name subConfig subApi) where
type ServerT (WithNamedConfig name subConfig subApi) m =
ServerT subApi m
type HasConfig (WithNamedConfig name subConfig subApi) config =
(HasConfigEntry config (NamedConfig name subConfig), HasConfig subApi subConfig)
route Proxy config delayed =
route subProxy subConfig delayed
where
subProxy :: Proxy subApi
subProxy = Proxy
subConfig :: Config subConfig
subConfig = descendIntoNamedConfig (Proxy :: Proxy name) config

View file

@ -1,62 +1,57 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-} {-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-} {-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
#include "overlapping-compat.h" #include "overlapping-compat.h"
module Servant.Server.Internal.Config where module Servant.Server.Internal.Config where
import Control.DeepSeq (NFData) import Data.Proxy
import GHC.Generics (Generic) import GHC.TypeLits
import Data.Typeable (Typeable)
-- | A single entry in the configuration. The first parameter is phantom, and
-- is used to lookup a @ConfigEntry@ in a @Config@.
newtype ConfigEntry tag a = ConfigEntry { unConfigEntry :: a }
deriving ( Eq, Show, Read, Enum, Integral, Fractional, Generic, Typeable
, Num, Ord, Real, Functor, Foldable, Traversable, NFData)
-- | The entire configuration. -- | The entire configuration.
data Config a where data Config a where
EmptyConfig :: Config '[] EmptyConfig :: Config '[]
ConsConfig :: x -> Config xs -> Config (x ': xs) (:.) :: x -> Config xs -> Config (x ': xs)
infixr 5 :.
instance Show (Config '[]) where instance Show (Config '[]) where
show EmptyConfig = "EmptyConfig" show EmptyConfig = "EmptyConfig"
instance (Show a, Show (Config as)) => Show (Config (ConfigEntry tag a ': as)) where instance (Show a, Show (Config as)) => Show (Config (a ': as)) where
showsPrec outerPrecedence (ConsConfig (ConfigEntry a) as) = showsPrec outerPrecedence (a :. as) =
showParen (outerPrecedence > 5) $ showParen (outerPrecedence > 5) $
shows a . showString " .:. " . shows as shows a . showString " :. " . shows as
instance Eq (Config '[]) where instance Eq (Config '[]) where
_ == _ = True _ == _ = True
instance (Eq a, Eq (Config as)) => Eq (Config (a ': as)) where instance (Eq a, Eq (Config as)) => Eq (Config (a ': as)) where
ConsConfig x1 y1 == ConsConfig x2 y2 = x1 == x2 && y1 == y2 x1 :. y1 == x2 :. y2 = x1 == x2 && y1 == y2
(.:.) :: x -> Config xs -> Config (ConfigEntry tag x ': xs) class HasConfigEntry (config :: [*]) (val :: *) where
e .:. cfg = ConsConfig (ConfigEntry e) cfg getConfigEntry :: Config config -> val
infixr 5 .:.
class HasConfigEntry (cfg :: [*]) (a :: k) (val :: *) | cfg a -> val where
getConfigEntry :: proxy a -> Config cfg -> val
instance OVERLAPPABLE_ instance OVERLAPPABLE_
HasConfigEntry xs tag val => HasConfigEntry (notIt ': xs) tag val where HasConfigEntry xs val => HasConfigEntry (notIt ': xs) val where
getConfigEntry p (ConsConfig _ xs) = getConfigEntry p xs getConfigEntry (_ :. xs) = getConfigEntry xs
instance OVERLAPPABLE_ instance OVERLAPPING_
HasConfigEntry (ConfigEntry tag val ': xs) tag val where HasConfigEntry (val ': xs) val where
getConfigEntry _ (ConsConfig x _) = unConfigEntry x getConfigEntry (x :. _) = x
-- * support for named subconfigs
data NamedConfig (name :: Symbol) (subConfig :: [*])
= NamedConfig (Config subConfig)
descendIntoNamedConfig :: forall config name subConfig .
HasConfigEntry config (NamedConfig name subConfig) =>
Proxy (name :: Symbol) -> Config config -> Config subConfig
descendIntoNamedConfig Proxy config =
let NamedConfig subConfig = getConfigEntry config :: NamedConfig name subConfig
in subConfig

View file

@ -11,42 +11,51 @@ import Servant.Server.Internal.Config
spec :: Spec spec :: Spec
spec = do spec = do
getConfigEntrySpec describe "getConfigEntry" $ do
it "gets the config if a matching one exists" $ do
let config = 'a' :. EmptyConfig
getConfigEntry config `shouldBe` 'a'
getConfigEntrySpec :: Spec it "gets the first matching config" $ do
getConfigEntrySpec = describe "getConfigEntry" $ do let config = 'a' :. 'b' :. EmptyConfig
getConfigEntry config `shouldBe` 'a'
let cfg1 = 0 .:. EmptyConfig :: Config '[ConfigEntry "a" Int] it "does not typecheck if type does not exist" $ do
cfg2 = 1 .:. cfg1 :: Config '[ConfigEntry "a" Int, ConfigEntry "a" Int] let config = 'a' :. EmptyConfig
x = getConfigEntry config :: Bool
shouldNotTypecheck x
it "gets the config if a matching one exists" $ do context "Show instance" $ do
let config = 'a' :. True :. EmptyConfig
it "has a Show instance" $ do
show config `shouldBe` "'a' :. True :. EmptyConfig"
getConfigEntry (Proxy :: Proxy "a") cfg1 `shouldBe` 0 context "bracketing" $ do
it "works" $ do
show (Just config) `shouldBe` "Just ('a' :. True :. EmptyConfig)"
it "gets the first matching config" $ do it "works with operators" $ do
let config = (1 :. 'a' :. EmptyConfig) :<|> ('b' :. True :. EmptyConfig)
show config `shouldBe` "(1 :. 'a' :. EmptyConfig) :<|> ('b' :. True :. EmptyConfig)"
getConfigEntry (Proxy :: Proxy "a") cfg2 `shouldBe` 1 describe "descendIntoNamedConfig" $ do
let config :: Config [Char, NamedConfig "sub" '[Char]]
config =
'a' :.
(NamedConfig subConfig :: NamedConfig "sub" '[Char])
:. EmptyConfig
subConfig = 'b' :. EmptyConfig
it "allows extracting subconfigs" $ do
descendIntoNamedConfig (Proxy :: Proxy "sub") config `shouldBe` subConfig
it "allows to distinguish between different config entries with the same type by tag" $ do it "allows extracting entries from subconfigs" $ do
let cfg = 'a' .:. 'b' .:. EmptyConfig :: Config '[ConfigEntry 1 Char, ConfigEntry 2 Char] getConfigEntry (descendIntoNamedConfig (Proxy :: Proxy "sub") config :: Config '[Char])
getConfigEntry (Proxy :: Proxy 1) cfg `shouldBe` 'a' `shouldBe` 'b'
context "Show instance" $ do it "does not typecheck if subConfig has the wrong type" $ do
let cfg = 1 .:. 2 .:. EmptyConfig let x = descendIntoNamedConfig (Proxy :: Proxy "sub") config :: Config '[Int]
it "has a Show instance" $ do shouldNotTypecheck (show x)
show cfg `shouldBe` "1 .:. 2 .:. EmptyConfig"
it "bracketing works" $ do it "does not typecheck if subConfig with that name doesn't exist" $ do
show (Just cfg) `shouldBe` "Just (1 .:. 2 .:. EmptyConfig)" let x = descendIntoNamedConfig (Proxy :: Proxy "foo") config :: Config '[Char]
shouldNotTypecheck (show x)
it "bracketing works with operators" $ do
let cfg = (1 .:. 'a' .:. EmptyConfig) :<|> ('b' .:. True .:. EmptyConfig)
show cfg `shouldBe` "(1 .:. 'a' .:. EmptyConfig) :<|> ('b' .:. True .:. EmptyConfig)"
it "does not typecheck if key does not exist" $ do
let x = getConfigEntry (Proxy :: Proxy "b") cfg1 :: Int
shouldNotTypecheck x
it "does not typecheck if key maps to a different type" $ do
let x = getConfigEntry (Proxy :: Proxy "a") cfg1 :: String
shouldNotTypecheck x

View file

@ -0,0 +1,125 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
module Servant.Server.UsingConfigSpec where
import Control.Monad.Trans.Except
import Network.Wai
import Test.Hspec (Spec, describe, it)
import Test.Hspec.Wai
import Servant
import Servant.Server.UsingConfigSpec.TestCombinators
spec :: Spec
spec = do
spec1
spec2
spec3
spec4
-- * API
type OneEntryAPI =
ExtractFromConfig :> Get '[JSON] String
testServer :: String -> ExceptT ServantErr IO String
testServer s = return s
oneEntryApp :: Application
oneEntryApp =
serve (Proxy :: Proxy OneEntryAPI) config testServer
where
config :: Config '[String]
config = "configEntry" :. EmptyConfig
type OneEntryTwiceAPI =
"foo" :> ExtractFromConfig :> Get '[JSON] String :<|>
"bar" :> ExtractFromConfig :> Get '[JSON] String
oneEntryTwiceApp :: Application
oneEntryTwiceApp = serve (Proxy :: Proxy OneEntryTwiceAPI) config $
testServer :<|>
testServer
where
config :: Config '[String]
config = "configEntryTwice" :. EmptyConfig
-- * tests
spec1 :: Spec
spec1 = do
describe "accessing config entries from custom combinators" $ do
with (return oneEntryApp) $ do
it "allows retrieving a ConfigEntry" $ do
get "/" `shouldRespondWith` "\"configEntry\""
with (return oneEntryTwiceApp) $ do
it "allows retrieving the same ConfigEntry twice" $ do
get "/foo" `shouldRespondWith` "\"configEntryTwice\""
get "/bar" `shouldRespondWith` "\"configEntryTwice\""
type InjectAPI =
InjectIntoConfig :> "untagged" :> ExtractFromConfig :>
Get '[JSON] String :<|>
InjectIntoConfig :> "tagged" :> ExtractFromConfig :>
Get '[JSON] String
injectApp :: Application
injectApp = serve (Proxy :: Proxy InjectAPI) config $
(\ s -> return s) :<|>
(\ s -> return ("tagged: " ++ s))
where
config = EmptyConfig
spec2 :: Spec
spec2 = do
with (return injectApp) $ do
describe "inserting config entries with custom combinators" $ do
it "allows to inject config entries" $ do
get "/untagged" `shouldRespondWith` "\"injected\""
it "allows to inject tagged config entries" $ do
get "/tagged" `shouldRespondWith` "\"tagged: injected\""
type WithBirdfaceAPI =
"foo" :> ExtractFromConfig :> Get '[JSON] String :<|>
NamedConfigWithBirdface "sub" '[String] :>
"bar" :> ExtractFromConfig :> Get '[JSON] String
withBirdfaceApp :: Application
withBirdfaceApp = serve (Proxy :: Proxy WithBirdfaceAPI) config $
testServer :<|>
testServer
where
config :: Config '[String, (NamedConfig "sub" '[String])]
config =
"firstEntry" :.
(NamedConfig ("secondEntry" :. EmptyConfig)) :.
EmptyConfig
spec3 :: Spec
spec3 = do
with (return withBirdfaceApp) $ do
it "allows retrieving different ConfigEntries for the same combinator" $ do
get "/foo" `shouldRespondWith` "\"firstEntry\""
get "/bar" `shouldRespondWith` "\"secondEntry\""
type NamedConfigAPI =
WithNamedConfig "sub" '[String] (
ExtractFromConfig :> Get '[JSON] String)
namedConfigApp :: Application
namedConfigApp = serve (Proxy :: Proxy NamedConfigAPI) config return
where
config :: Config '[NamedConfig "sub" '[String]]
config = NamedConfig ("descend" :. EmptyConfig) :. EmptyConfig
spec4 :: Spec
spec4 = do
with (return namedConfigApp) $ do
describe "WithNamedConfig" $ do
it "allows descending into a subconfig for a given api" $ do
get "/" `shouldRespondWith` "\"descend\""

View file

@ -0,0 +1,78 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
-- | These are custom combinators for Servant.Server.UsingConfigSpec.
--
-- (For writing your own combinators you need to import Internal modules, for
-- just *using* combinators that require a Config, you don't. This module is
-- separate from Servant.Server.UsingConfigSpec to test that the module imports
-- work out this way.)
module Servant.Server.UsingConfigSpec.TestCombinators where
import GHC.TypeLits
import Servant
import Servant.Server.Internal.Config
import Servant.Server.Internal.RoutingApplication
data ExtractFromConfig
instance (HasServer subApi) =>
HasServer (ExtractFromConfig :> subApi) where
type ServerT (ExtractFromConfig :> subApi) m =
String -> ServerT subApi m
type HasConfig (ExtractFromConfig :> subApi) (c :: [*]) =
(HasConfigEntry c String, HasConfig subApi c)
route Proxy config delayed =
route subProxy config (fmap (inject config) delayed :: Delayed (Server subApi))
where
subProxy :: Proxy subApi
subProxy = Proxy
inject config f = f (getConfigEntry config)
data InjectIntoConfig
instance (HasServer subApi) =>
HasServer (InjectIntoConfig :> subApi) where
type ServerT (InjectIntoConfig :> subApi) m =
ServerT subApi m
type HasConfig (InjectIntoConfig :> subApi) c =
(HasConfig subApi (String ': c))
route Proxy config delayed =
route subProxy newConfig delayed
where
subProxy :: Proxy subApi
subProxy = Proxy
newConfig = ("injected" :: String) :. config
data NamedConfigWithBirdface (name :: Symbol) (subConfig :: [*])
instance (HasServer subApi) =>
HasServer (NamedConfigWithBirdface name subConfig :> subApi) where
type ServerT (NamedConfigWithBirdface name subConfig :> subApi) m =
ServerT subApi m
type HasConfig (NamedConfigWithBirdface name subConfig :> subApi) config =
(HasConfigEntry config (NamedConfig name subConfig), HasConfig subApi subConfig)
route Proxy config delayed =
route subProxy subConfig delayed
where
subProxy :: Proxy subApi
subProxy = Proxy
subConfig :: Config subConfig
subConfig = descendIntoNamedConfig (Proxy :: Proxy name) config

View file

@ -48,10 +48,10 @@ import Servant.API ((:<|>) (..), (:>), AuthProtect, Bas
QueryFlag, QueryParam, QueryParams, QueryFlag, QueryParam, QueryParams,
Raw, RemoteHost, ReqBody, Raw, RemoteHost, ReqBody,
StdMethod (..), Verb, addHeader) StdMethod (..), Verb, addHeader)
import Servant.Server ((.:.), AuthHandler, AuthReturnType, import Servant.Server (AuthHandler, AuthReturnType,
BasicAuthCheck (BasicAuthCheck), BasicAuthCheck (BasicAuthCheck),
BasicAuthResult (Authorized, Unauthorized), BasicAuthResult (Authorized, Unauthorized),
Config (EmptyConfig), ConfigEntry, ServantErr (..), Config ((:.), EmptyConfig), ServantErr (..),
mkAuthHandler, Server, err401, err404, serve) mkAuthHandler, Server, err401, err404, serve)
import Test.Hspec (Spec, context, describe, it, import Test.Hspec (Spec, context, describe, it,
shouldBe, shouldContain) shouldBe, shouldContain)
@ -226,7 +226,7 @@ qpServer = queryParamServer :<|> qpNames :<|> qpCapitalize
queryParamSpec :: Spec queryParamSpec :: Spec
queryParamSpec = do queryParamSpec = do
describe "Servant.API.QueryParam" $ do describe "Servant.API.QueryParam" $ do
it "allows to retrieve simple GET parameters" $ it "allows retrieving simple GET parameters" $
(flip runSession) (serve queryParamApi EmptyConfig qpServer) $ do (flip runSession) (serve queryParamApi EmptyConfig qpServer) $ do
let params1 = "?name=bob" let params1 = "?name=bob"
response1 <- Network.Wai.Test.request defaultRequest{ response1 <- Network.Wai.Test.request defaultRequest{
@ -238,7 +238,7 @@ queryParamSpec = do
name = "bob" name = "bob"
} }
it "allows to retrieve lists in GET parameters" $ it "allows retrieving lists in GET parameters" $
(flip runSession) (serve queryParamApi EmptyConfig qpServer) $ do (flip runSession) (serve queryParamApi EmptyConfig qpServer) $ do
let params2 = "?names[]=bob&names[]=john" let params2 = "?names[]=bob&names[]=john"
response2 <- Network.Wai.Test.request defaultRequest{ response2 <- Network.Wai.Test.request defaultRequest{
@ -252,7 +252,7 @@ queryParamSpec = do
} }
it "allows to retrieve value-less GET parameters" $ it "allows retrieving value-less GET parameters" $
(flip runSession) (serve queryParamApi EmptyConfig qpServer) $ do (flip runSession) (serve queryParamApi EmptyConfig qpServer) $ do
let params3 = "?capitalize" let params3 = "?capitalize"
response3 <- Network.Wai.Test.request defaultRequest{ response3 <- Network.Wai.Test.request defaultRequest{
@ -339,13 +339,13 @@ headerSpec = describe "Servant.API.Header" $ do
expectsString Nothing = error "Expected a string" expectsString Nothing = error "Expected a string"
with (return (serve headerApi EmptyConfig expectsInt)) $ do with (return (serve headerApi EmptyConfig expectsInt)) $ do
let delete' x = Test.Hspec.Wai.request methodDelete x [("MyHeader" ,"5")] let delete' x = Test.Hspec.Wai.request methodDelete x [("MyHeader", "5")]
it "passes the header to the handler (Int)" $ it "passes the header to the handler (Int)" $
delete' "/" "" `shouldRespondWith` 200 delete' "/" "" `shouldRespondWith` 200
with (return (serve headerApi EmptyConfig expectsString)) $ do with (return (serve headerApi EmptyConfig expectsString)) $ do
let delete' x = Test.Hspec.Wai.request methodDelete x [("MyHeader" ,"more from you")] let delete' x = Test.Hspec.Wai.request methodDelete x [("MyHeader", "more from you")]
it "passes the header to the handler (String)" $ it "passes the header to the handler (String)" $
delete' "/" "" `shouldRespondWith` 200 delete' "/" "" `shouldRespondWith` 200
@ -537,8 +537,8 @@ authServer = const (return jerry) :<|> const (return tweety)
type instance AuthReturnType (AuthProtect "auth") = () type instance AuthReturnType (AuthProtect "auth") = ()
authConfig :: Config '[ ConfigEntry "basic" (BasicAuthCheck ()) authConfig :: Config '[ BasicAuthCheck ()
, ConfigEntry "auth" (AuthHandler Request ()) , AuthHandler Request ()
] ]
authConfig = authConfig =
let basicHandler = BasicAuthCheck $ (\usr pass -> let basicHandler = BasicAuthCheck $ (\usr pass ->
@ -551,7 +551,7 @@ authConfig =
then return () then return ()
else throwE err401 else throwE err401
) )
in basicHandler .:. mkAuthHandler authHandler .:. EmptyConfig in basicHandler :. mkAuthHandler authHandler :. EmptyConfig
authSpec :: Spec authSpec :: Spec
authSpec = do authSpec = do

1
servant/.ghci Normal file
View file

@ -0,0 +1 @@
:set -isrc -itest -Iinclude -optP-include -optPdist/build/autogen/cabal_macros.h

View file

@ -41,6 +41,7 @@ library
Servant.API.Sub Servant.API.Sub
Servant.API.Vault Servant.API.Vault
Servant.API.Verbs Servant.API.Verbs
Servant.API.WithNamedConfig
Servant.Utils.Links Servant.Utils.Links
build-depends: build-depends:
base >=4.7 && <5 base >=4.7 && <5

View file

@ -23,6 +23,8 @@ module Servant.API (
-- | Is the request made through HTTPS? -- | Is the request made through HTTPS?
module Servant.API.Vault, module Servant.API.Vault,
-- | Access the location for arbitrary data to be shared by applications and middleware -- | Access the location for arbitrary data to be shared by applications and middleware
module Servant.API.WithNamedConfig,
-- | Access config entries in combinators in servant-server
-- * Actual endpoints, distinguished by HTTP method -- * Actual endpoints, distinguished by HTTP method
module Servant.API.Verbs, module Servant.API.Verbs,
@ -92,6 +94,7 @@ import Servant.API.Verbs (PostCreated, Delete, DeleteAccepte
PutNoContent, PutNonAuthoritative, PutNoContent, PutNonAuthoritative,
ReflectMethod (reflectMethod), ReflectMethod (reflectMethod),
Verb, StdMethod(..)) Verb, StdMethod(..))
import Servant.API.WithNamedConfig (WithNamedConfig)
import Servant.Utils.Links (HasLink (..), IsElem, IsElem', import Servant.Utils.Links (HasLink (..), IsElem, IsElem',
URI (..), safeLink) URI (..), safeLink)
import Web.HttpApiData (FromHttpApiData (..), import Web.HttpApiData (FromHttpApiData (..),

View file

@ -0,0 +1,8 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
module Servant.API.WithNamedConfig where
import GHC.TypeLits
data WithNamedConfig (name :: Symbol) (subConfig :: [*]) subApi