hunt-server-0.3.0.2: src/Hunt/Server/Schrotty.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GADTs #-}
-- ----------------------------------------------------------------------------
{- |
Schrotty server.
Scotty with custom error handling.
All regular scotty functions can be used.
-}
-- ----------------------------------------------------------------------------
module Hunt.Server.Schrotty
(
-- * Start the server
schrotty
, schrottyOpts
-- * Adjusted/Added Scotty Functions
, jsonData
, param
, jsonPretty
-- * Types
, module Web.Scotty.Trans
, ActionSchrotty
, ActionSchrottyT
, WebError(..)
)
where
import Network.HTTP.Types
import Network.Wai.Handler.Warp (Port)
import Web.Scotty.Trans hiding (jsonData, param)
import qualified Web.Scotty.Trans as Scotty
import Data.Text.Lazy (Text)
import qualified Data.Text.Lazy as TL
import Data.Aeson as A
import Data.Aeson.Encode.Pretty as AP
import Hunt.Interpreter.Command (CmdError (..))
import Hunt.Server.Common
-- ------------------------------------------------------------
type ActionSchrottyT m a = ActionT WebError m a
type ActionSchrotty a = ActionT WebError IO a
data WebError
= InterpreterError CmdError
| NotFound
| JsonInvalid String
| MissingParam Text
| Other Text
| Text Int Text
| forall a . ToJSON a =>
Json Int a
-- XXX: no Show instance because it's not a Haskell98 type
instance ScottyError WebError where
stringError = Text 500 . TL.pack
showError _ = "schrotty exception :("
-- ------------------------------------------------------------
handleCustomError :: Monad m => WebError -> ActionSchrottyT m ()
handleCustomError (NotFound)
= do
status status404
text $ "Not found."
handleCustomError (JsonInvalid s)
= do
status status400
jsonPretty $ JsonFailure 400 ("json invalid: " ++ s)
handleCustomError (MissingParam txt)
= do
status status404
text $ "Query parameter missing " `TL.append` txt -- TODO: fix types
handleCustomError (Other txt)
= do
status status500
text $ txt
handleCustomError (Text code bs)
= do
status (toEnum code)
text $ bs
handleCustomError (Json code o)
= do
status (toEnum code)
jsonPretty $ JsonFailure code o
handleCustomError (InterpreterError (ResError code msg))
= handleCustomError (Json code msg)
-- | Like 'Web.Scotty.json', but pretty.
jsonPretty :: (ToJSON a, ScottyError e, Monad m) => a -> ActionT e m ()
jsonPretty v = do
setHeader "Content-Type" "application/json"
raw $ AP.encodePretty v
-- | Replacement for 'Web.Scotty.jsonData' with custom error.
jsonData :: (FromJSON a, Monad m) => ActionSchrottyT m a
jsonData = do
b <- body
case A.eitherDecode b of
Right j -> return j
Left e -> raise $ JsonInvalid e
-- | Replacement for 'Web.Scotty.param' with custom error.
param :: (Parsable a, Monad m) => TL.Text -> ActionSchrottyT m a
param p = Scotty.param p
`rescue` (\_ -> raise $ MissingParam p)
-- ------------------------------------------------------------
-- | Start schrotty.
schrotty :: Port -> ScottyT WebError IO () -> IO ()
schrotty p a = Scotty.scottyT p id id $ defaultHandler handleCustomError >> a
-- | Start schrotty with options.
schrottyOpts :: Options -> ScottyT WebError IO () -> IO ()
schrottyOpts o a = Scotty.scottyOptsT o id id $ defaultHandler handleCustomError >> a
-- ------------------------------------------------------------