{-| Module : Servant.Server.Auth.Token.Monad Description : Monad for auth server handler Copyright : (c) Anton Gushcha, 2016 License : MIT Maintainer : ncrashed@gmail.com Stability : experimental Portability : Portable -} module Servant.Server.Auth.Token.Monad( AuthHandler(..) , require , getConfig , getsConfig , runDB404 , module Reexport ) where import Control.Monad.Except (ExceptT, MonadError) import Control.Monad.Reader (MonadIO, MonadReader, ReaderT, ask, asks) import Data.Monoid ((<>)) import Database.Persist.Postgresql import Servant import qualified Data.ByteString.Lazy as BS import Servant.Server.Auth.Token.Config import Servant.Server.Auth.Token.Model import Servant.Server.Auth.Token.Error as Reexport -- | This type represents the effects we want to have for our application. -- We wrap the standard Servant monad with 'ReaderT Config', which gives us -- access to the application configuration using the 'MonadReader' -- interface's 'ask' function. -- -- By encapsulating the effects in our newtype, we can add layers to the -- monad stack without having to modify code that uses the current layout. newtype AuthHandler a = AuthHandler { runAuthHandler :: ReaderT AuthConfig (ExceptT ServantErr IO) a } deriving ( Functor, Applicative, Monad, MonadReader AuthConfig, MonadError ServantErr, MonadIO) -- | If the value is 'Nothing', throw 400 response require :: BS.ByteString -> Maybe a -> AuthHandler a require info Nothing = throw400 $ info <> " is required" require _ (Just a) = return a -- | Getting config from global state getConfig :: AuthHandler AuthConfig getConfig = ask -- | Getting config part from global state getsConfig :: (AuthConfig -> a) -> AuthHandler a getsConfig = asks -- | Run RDBMS operation and throw 404 (not found) error if -- the second arg returns 'Nothing' runDB404 :: BS.ByteString -> SqlPersistT IO (Maybe a) -> AuthHandler a runDB404 info ma = do a <- runDB ma case a of Nothing -> throw404 $ "Cannot find " <> info Just a' -> return a'