-- 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