{-# LANGUAGE
DeriveDataTypeable
, DeriveGeneric
, LambdaCase
, OverloadedStrings
, ScopedTypeVariables
#-}
module Api.Test
( resource
, WithText
, Err (..)
, Ok (..)
) where
import Control.Monad.Except
import Control.Monad.Reader
import Data.Aeson
import Data.ByteString.Lazy (ByteString)
import Data.Data
import Data.JSON.Schema
import Data.Maybe
import Data.Text (Text)
import GHC.Generics
import Generics.Generic.Aeson
import Generics.XmlPickler
import Safe
import Text.XML.HXT.Arrow.Pickle
import Rest
import Rest.Dictionary
import qualified Rest.Driver.Perform as Driver (accept)
import qualified Rest.Resource as R
import ApiTypes
import qualified Api.Test.Err2 as E2
-- | User extends the root of the API with a reader containing the ways to identify a user in our URLs.
-- Currently only by the user name.
type WithText = ReaderT Text BlogApi
data Err = Err deriving (Generic, Show, Typeable)
instance ToJSON Err where toJSON = gtoJson
instance FromJSON Err where parseJSON = gparseJson
instance JSONSchema Err where schema = gSchema
instance XmlPickler Err where xpickle = gxpickle
instance ToResponseCode Err where
toResponseCode _ = 400
data Ok = Ok deriving (Generic, Show, Typeable)
instance XmlPickler Ok where xpickle = gxpickle
instance ToJSON Ok where toJSON = gtoJson
instance FromJSON Ok where parseJSON = gparseJson
instance JSONSchema Ok where schema = gSchema
resource :: Resource BlogApi WithText Text Void Void
resource = mkResourceReader
{ R.name = "test"
, R.actions = [ ("noResponse" , noResponse )
, ("onlyError" , onlyError )
, ("differentFormats" , differentFormats )
, ("intersectedFormats" , intersectedFormats )
, ("intersectedFormats2", intersectedFormats2)
, ("rawXmlIO" , rawXmlIO )
, ("rawJsonIO" , rawJsonIO )
, ("rawJsonAndXmlI" , rawJsonAndXmlI_ )
, ("rawJsonAndXmlO" , rawJsonAndXmlO_ )
, ("noError" , noError )
, ("justStringO" , justStringO )
, ("preferJson" , preferJson )
, ("octetStreamOut" , octetStreamOut )
, ("onlyInput" , onlyInput )
]
}
noResponse :: Handler WithText
noResponse = mkConstHandler id $ return ()
onlyError :: Handler WithText
onlyError = mkConstHandler jsonE $
throwError $ domainReason Err
differentFormats :: Handler WithText
differentFormats = mkInputHandler (jsonE . xmlO . stringI) $
\case
"error" -> throwError $ domainReason Err
_ -> return Ok
intersectedFormats :: Handler WithText
intersectedFormats = mkInputHandler (jsonE . xmlO . jsonO . stringI) $
\case
"error" -> throwError $ domainReason Err
_ -> return Ok
intersectedFormats2 :: Handler WithText
intersectedFormats2 = mkInputHandler (xmlE . xmlO . jsonO . stringI) $
\case
"error" -> throwError $ domainReason Err
_ -> return Ok
rawXmlIO :: Handler WithText
rawXmlIO = mkIdHandler (rawXmlI . rawXmlO . xmlE) $ \s _ ->
case s of
"" -> throwError $ domainReason E2.Err
_ -> return ""
rawJsonIO :: Handler WithText
rawJsonIO = mkIdHandler (rawJsonI . rawJsonO . jsonE) $ \s _ ->
case s of
"\"error\"" -> throwError $ domainReason E2.Err
_ -> return "\"ok\""
rawJsonAndXmlI_ :: Handler WithText
rawJsonAndXmlI_ = mkInputHandler (stringO . rawJsonAndXmlI) handler
where
handler :: Either Json Xml -> ExceptT Reason_ WithText String
handler = return . \case
Left (Json _) -> "json input"
Right (Xml _) -> "xml input"
rawJsonAndXmlO_ :: Handler WithText
rawJsonAndXmlO_ = mkHandler (addHeader contentType . mkHeader accept . mkPar typeParam . rawJsonAndXmlO) handler
where
handler :: Env (Maybe String, Maybe String) (Maybe String) () -> ExceptT Reason_ WithText ByteString
handler (Env (mContentType, mAccept) mType ()) = do
let accs = Driver.accept mAccept mContentType mType
if JsonFormat `elem` accs
then return "\"json\""
else if XmlFormat `elem` accs
then return ""
else throwError . OutputError $ UnsupportedFormat "Only json and xml accept headers are allowed"
contentType :: Header (Maybe String)
contentType = Header ["Content-Type"] (return . headMay . catMaybes)
typeParam :: Param (Maybe String)
typeParam = Param ["type"] (return . headMay . catMaybes)
accept :: Header (Maybe String)
accept = Header ["Accept"] (return . headMay . catMaybes)
noError :: Handler WithText
noError = mkConstHandler jsonO $ return Ok
justStringO :: Handler WithText
justStringO = mkConstHandler stringO $ return "Ok"
preferJson :: Handler WithText
preferJson = mkInputHandler (xmlJsonO . xmlJsonE . stringI) $
\case
"error" -> throwError $ domainReason Err
_ -> return Ok
octetStreamOut :: Handler WithText
octetStreamOut = mkInputHandler (fileI . fileO . xmlJsonE) $
\case
"error" -> throwError $ domainReason Err
_ -> return ("ok", "ok", False)
onlyInput :: Handler WithText
onlyInput = mkInputHandler jsonI $ \() -> throwError NotFound