-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Extra servant combinators for full WAI functionality. -- -- Servant covers most of the data in a raw WAI request, but misses a few -- fields. This library aims to let handler authors get all the -- information about a request they need. @package servant-combinators @version 0.0.2 module Servant.API.Cookies -- | A SessionMap is a hash map of session data from a request. type SessionMap = Map ByteString ByteString -- | A SetCookieHeader is a convenience type for adding a "Set-Cookie" -- header that expects a SetCookie record type. -- -- I wanted to have the header name be NTH.hSetCookie for extra "use the -- known correct value" goodness, but that breaks the type magic Servant -- relies upon. type SetCookieHeader a = Headers '[Header "Set-Cookie" SetCookie] a -- | The ProvideCookies and WithCookies combinator work -- in tandem together -- the ProvideCookies combinator parses -- the cookies from the request and stores them in the WAI request Vault, -- the WithCookies combinator provides the cookies as a hash map -- to the handler. data ProvideCookies (mods :: [Type]) -- | As mentioned above, the WithCookies combinator provides -- already-parsed cookies to the handler as a SessionMap. -- -- The cookie values are assumed to be encrypted with a -- Web.ClientSession.Key. Likewise, updateCookies -- encrypts the cookies on the outbound side via this mechanism. -- -- Example: -- --
-- import Control.Monad.IO.Class (liftIO)
-- import Servant
-- import ServantExtras.Cookies
--
-- import qualified Data.Map.Strict as Map
--
-- type MyAPI = "my-cookie-enabled-endpoint"
-- :> ProvideCookies '[Required]
-- :> WithCookies '[Required]
-- :> Get '[JSON] NoContent
--
-- myServer :: Server MyAPI
-- myServer = cookieEndpointHandler
-- where
-- cookieEndpointHandler :: SessionMap -> Handler NoContent
-- cookieEndpointHandler sMap =
-- let mCookieValue = lookup MerlinWasHere $ Map.toList sMap in
-- case mCookieValue of
-- Nothing -> do
-- liftIO $ print "Merlin was *NOT* here!"
-- throwError err400 { errBody = "Clearly you've missed something." }
-- Just message -> do
-- liftIO $ do
-- print "Merlin WAS here, and he left us a message!"
-- print message
-- pure NoContent
--
data WithCookies (mods :: [Type])
-- | HasCookies and HasCookiesMaybe are internal utitily
-- types. You should only need to use ProvideCookies and
-- WithCookies.
--
-- As an aside, they're separate types (rather than a single type with a
-- (mods :: [Type]) ) phantom type because the term-level values show up
-- in the instances, and I didn't see a clean way to separate them out by
-- case, and only covering one value from the sum type made Haskell
-- (rightly) complain.
data HasCookies
HasCookies :: HasCookies
-- | HasCookies and HasCookiesMaybe are internal utitily
-- types. You should only need to use ProvideCookies and
-- WithCookies.
data HasCookiesMaybe
HasCookiesMaybe :: HasCookiesMaybe
-- | This function takes a SessionMap and provides a "Set-Cookie" header to
-- set the SessionData to a newly minted value of your choice.
updateCookies :: Key -> SessionMap -> SetCookie -> ByteString -> a -> IO (SetCookieHeader a)
-- | This function clears session data, for a fresh, minty-clean
-- experience. The archetypal use case is when a user logs out from your
-- server.
clearSession :: SetCookie -> a -> IO (SetCookieHeader a)
instance (Servant.Server.Internal.HasServer api (Servant.API.Cookies.HasCookiesMaybe : ctx), Servant.Server.Internal.Context.HasContextEntry ctx (Data.Vault.Lazy.Key (GHC.Maybe.Maybe Servant.API.Cookies.SessionMap)), Servant.Server.Internal.Context.HasContextEntry ctx Web.ClientSession.Key) => Servant.Server.Internal.HasServer (Servant.API.Cookies.ProvideCookies '[Servant.API.Modifiers.Optional] Servant.API.Sub.:> api) ctx
instance (Servant.Server.Internal.HasServer api ctx, Servant.Server.Internal.Context.HasContextEntry ctx Servant.API.Cookies.HasCookiesMaybe, Servant.Server.Internal.Context.HasContextEntry ctx (Data.Vault.Lazy.Key (GHC.Maybe.Maybe Servant.API.Cookies.SessionMap))) => Servant.Server.Internal.HasServer (Servant.API.Cookies.WithCookies '[Servant.API.Modifiers.Optional] Servant.API.Sub.:> api) ctx
instance (Servant.Server.Internal.HasServer api (Servant.API.Cookies.HasCookies : ctx), Servant.Server.Internal.Context.HasContextEntry ctx (Data.Vault.Lazy.Key Servant.API.Cookies.SessionMap), Servant.Server.Internal.Context.HasContextEntry ctx Web.ClientSession.Key) => Servant.Server.Internal.HasServer (Servant.API.Cookies.ProvideCookies '[Servant.API.Modifiers.Required] Servant.API.Sub.:> api) ctx
instance (Servant.Server.Internal.HasServer api ctx, Servant.Server.Internal.Context.HasContextEntry ctx Servant.API.Cookies.HasCookies, Servant.Server.Internal.Context.HasContextEntry ctx (Data.Vault.Lazy.Key Servant.API.Cookies.SessionMap)) => Servant.Server.Internal.HasServer (Servant.API.Cookies.WithCookies '[Servant.API.Modifiers.Required] Servant.API.Sub.:> api) ctx
module Servant.API.HeaderList
-- | The HeaderList combinator provides a list of
-- Network.HTTP.Types.Header.Header values from the WAI request.
--
-- Example:
--
--
-- import Control.Monad.IO.Class (liftIO)
-- import Servant
-- import ServantExtras.HeaderList
--
-- import qualified Network.HTTP.Types.Header as NTH (Header)
--
-- type MyAPI = "my-header-endpoint"
-- :> HeaderList
-- :> Get '[JSON] NoContent
--
-- myServer :: Server MyAPI
-- myServer = headerEndpointHandler
-- where
-- headerEndpointHandler :: [NTH.Header] -> Handler NoContent
-- headerEndpointHandler headers =
-- let mCookieValue = lookup "merlinWasHere" headers in
-- case mCookieValue of
-- Nothing -> do
-- liftIO $ print "Merlin was *NOT* here!"
-- throwError err400 { errBody = "Clearly you've missed something." }
-- Just message -> do
-- liftIO $ do
-- print "Merlin WAS here, and he left us a message!"
-- print message
-- pure NoContent
--
data HeaderList
instance Servant.Server.Internal.HasServer api ctx => Servant.Server.Internal.HasServer (Servant.API.HeaderList.HeaderList Servant.API.Sub.:> api) ctx
module Servant.API.PathInfo
-- | PathInfo provides handlers access to the path segments from
-- the request, without the domain name or query parameters. We
-- re-generate this from the rawPathInfo via
-- Network.HTTP.Types.decodePathSegments because Servant removes
-- all fields from the pathInfo field of a request as part of
-- routing the request to the appropriate handler.
--
-- Example:
--
--
-- import Data.ByteString (ByteString)
-- import Control.Monad.IO.Class (liftIO)
-- import Servant
-- import ServantExtras.RawPathInfo
--
-- type MyAPI = "merlin" :> "my-path-info-endpoint"
-- :> PathInfo
-- :> Get '[JSON] NoContent
--
-- myServer :: Server MyAPI
-- myServer = pathInfoEndpointHandler
-- where
-- pathInfoEndpointHandler :: [Text] -> Handler NoContent
-- pathInfoEndpointHandler pInfo = do
-- case (elem "merlin" pInfo) of
-- False -> do
-- liftIO $ print "This example has a bug!"
-- throwError err400 { errBody = "Patches accepted!" }
-- True -> do
-- liftIO $ print "Hopefully this demonstrates how path info works."
-- pure NoContent
--
data PathInfo
instance Servant.Server.Internal.HasServer api ctx => Servant.Server.Internal.HasServer (Servant.API.PathInfo.PathInfo Servant.API.Sub.:> api) ctx
module Servant.API.QueryString
-- | QueryString provides handlers access to the full query string
-- from the WAI request, rather than pulling each element explicitly.
-- This allows for dynamic query management, or to simply take in many
-- queries in one argument.
--
-- Example:
--
--
-- import Control.Monad.IO.Class (liftIO)
-- import Network.HTTP.Types (Query, renderQuery)
-- import Servant
-- import ServantExtras.QueryString
--
-- type MyAPI = "my-cookie-enabled-endpoint"
-- :> QueryString
-- :> Get '[JSON] NoContent
--
-- myServer :: Server MyAPI
-- myServer = queryEndpointHandler
-- where
-- queryEndpointHandler :: Query -> Handler NoContent
-- queryEndpointHandler query = do
-- liftIO $ print $ renderQuery True query
-- let mCookieValue = lookup "merlinWasHere" query in
-- case mCookieValue of
-- Nothing -> do
-- liftIO $ print "Merlin was *NOT* here!"
-- throwError err400 { errBody = "Clearly you've missed something." }
-- Just message -> do
-- liftIO $ do
-- print "Merlin WAS here, and he left us a message!"
-- print message
-- pure NoContent
--
data QueryString
instance Servant.Server.Internal.HasServer api ctx => Servant.Server.Internal.HasServer (Servant.API.QueryString.QueryString Servant.API.Sub.:> api) ctx
module Servant.API.RawPathInfo
-- | RawPathInfo provides handlers access to the raw, unparsed
-- path information the WAI request.
--
-- If you wish to get the path segments, you can either use the
-- PathInfo combinator in Servant.API.PathInfo or parse
-- it yourself with Network.HTTP.Types.decodePathSegments
--
-- Example:
--
--
-- import Data.ByteString (ByteString)
-- import Control.Monad.IO.Class (liftIO)
-- import Servant
-- import ServantExtras.RawPathInfo
--
-- type MyAPI = "my-path-info-endpoint"
-- :> RawPathInfo
-- :> Get '[JSON] NoContent
--
-- myServer :: Server MyAPI
-- myServer = queryEndpointHandler
-- where
-- queryEndpointHandler :: ByteString -> Handler NoContent
-- queryEndpointHandler rawPath = do
-- case rawPath of
-- "/my-path-info-endpoint" -> do
-- liftIO $ print "Servant routed us to the right place!"
-- pure NoContent
-- _ -> do
-- liftIO $ print "My example has a bug!"
-- throwError err400 { errBody = "Patches accepted!" }
--
data RawPathInfo
instance Servant.Server.Internal.HasServer api ctx => Servant.Server.Internal.HasServer (Servant.API.RawPathInfo.RawPathInfo Servant.API.Sub.:> api) ctx
module Servant.API.RawQueryString
-- | RawQueryString gives handler authors a combinator to access
-- the raw (that is, un-parsed) query string from the WAI request, as a
-- ByteString.
--
-- Generally speaking, you should prefer to use the QueryString
-- combinator, but if you need access to the raw value, this combinator
-- provides it.
--
-- Example:
--
-- -- import Control.Monad.IO.Class (liftIO) -- import Servant -- import ServantExtras.RawQueryString -- -- import qualified Network.HTTP.Types.Header as NTH (Header) -- -- type MyAPI = "my-query-endpoint" -- :> RawQueryString -- :> Get '[JSON] NoContent -- -- myServer :: Server MyAPI -- myServer = queryEndpointHandler -- where -- queryEndpointHandler :: ByteString -> Handler NoContent -- queryEndpointHandler queryStr = -- -- do something with the ByteString, like pass it to a -- -- sub-process --data RawQueryString instance Servant.Server.Internal.HasServer api ctx => Servant.Server.Internal.HasServer (Servant.API.RawQueryString.RawQueryString Servant.API.Sub.:> api) ctx module Servant.API.RawRequest -- | RawRequest provides the Request field from the WAI -- request. -- -- Example: -- --
-- import Control.Monad.IO.Class (liftIO) -- import Network.Wai -- import Servant -- import ServantExtras.RawRequest -- -- type MyAPI = "my-request-endpoint" -- :> RawRequest -- :> Get '[JSON] NoContent -- -- myServer :: Server MyAPI -- myServer = requestEndpointHandler -- where -- requestEndpointHandler :: Request -> Handler NoContent -- requestEndpointHandler req = -- -- Do something clever with the request -- pure NoContent --data RawRequest instance Servant.Server.Internal.HasServer api ctx => Servant.Server.Internal.HasServer (Servant.API.RawRequest.RawRequest Servant.API.Sub.:> api) ctx