1
1
mirror of https://github.com/aelve/guide.git synced 2024-11-22 03:12:58 +03:00

Add custom monad for servant handlers

This commit is contained in:
Dmitry Kovanikov 2018-10-06 23:32:45 +08:00
parent c6b1ac17c7
commit 17268e0cd7
No known key found for this signature in database
GPG Key ID: 9824BEAFD9AF6A3E
4 changed files with 75 additions and 0 deletions

View File

@ -45,6 +45,8 @@ executable guide
library
exposed-modules:
Guide.App
Guide.App.Error
Guide.App.Monad
Guide.Api
Guide.Api.Methods
Guide.Api.Server
@ -184,6 +186,10 @@ library
, DeriveGeneric
, TypeApplications
, NoImplicitPrelude
, GeneralizedNewtypeDeriving
, ConstraintKinds
, InstanceSigs
, DerivingStrategies
test-suite tests
main-is: Main.hs

44
src/Guide/App/Error.hs Normal file
View File

@ -0,0 +1,44 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{- | Custom errors for servant handlers. -}
module Guide.App.Error
( WithError
, AppError (..)
, toHttpError
) where
import Imports
import Servant.Server (ServantErr, err401, err404, err417, err500, errBody)
-- | Type alias for errors.
type WithError m = MonadError AppError m
data AppError
-- | General not found
= NotFound
-- | Some exceptional circumstance has happened stop execution and
-- return. Optional text to provide some context in server logs
| ServerError Text
-- | A required permission level was not met. Optional text to provide some context.
| NotAllowed Text
-- | Given inputs do not conform to the expected format or shape. Optional
-- text to provide some context in server logs
| WrongArguments Text
-- | An authentication header that was required was provided but not in a
-- format that the server can understand
| HeaderError Text
deriving (Show, Eq)
deriving anyclass (Exception)
toHttpError :: AppError -> ServantErr
toHttpError = \case
NotFound -> err404
ServerError msg -> err500 { errBody = toLByteString msg }
NotAllowed msg -> err401 { errBody = toLByteString msg }
WrongArguments msg -> err417 { errBody = toLByteString msg }
HeaderError name -> err401 { errBody = toLByteString $ "Unable to decode header: " <> name }

22
src/Guide/App/Monad.hs Normal file
View File

@ -0,0 +1,22 @@
{- | Application monad for servant handlers. -}
module Guide.App.Monad
( GuideM (..)
) where
import Imports
import Guide.App.Error (AppError)
import Guide.Config (Config)
newtype GuideM a = GuideM
{ runGuideM :: ReaderT Config IO a
} deriving (Functor, Applicative, Monad, MonadIO, MonadReader Config)
instance MonadError AppError GuideM where
throwError :: AppError -> GuideM a
throwError = liftIO . throwIO
catchError :: GuideM a -> (AppError -> GuideM a) -> GuideM a
catchError = error "to be implemented"

View File

@ -6,6 +6,9 @@ packages:
nix:
shell-file: shell.nix
ghc-options:
"$locals": -fhide-source-paths
extra-deps:
- Spock-0.13.0.0
- Spock-core-0.13.0.0