add better server error messages, cbor vs raw hex

This commit is contained in:
Emily Martins 2022-06-27 13:30:27 +02:00
parent a5765a355d
commit 6a7e8369fd
7 changed files with 109 additions and 97 deletions

View file

@ -6,7 +6,8 @@
-}
module Data.Cache.Cached (
cached,
cachedFor,
cachedM,
cachedForM,
) where
import Control.Monad.IO.Class (MonadIO (liftIO))
@ -20,7 +21,14 @@ import System.Clock (TimeSpec)
Uses a HashMap under the hood.
-}
cached :: (Monad m, MonadIO m, Hashable k, Ord k) => (k -> v) -> IO (k -> m v)
cached = cachedFor Nothing
cached f = cachedForM Nothing (pure . f)
{- | 'cachedFor' but items last forever.
Uses a HashMap under the hood.
-}
cachedM :: (Monad m, MonadIO m, Hashable k, Ord k) => (k -> m v) -> IO (k -> m v)
cachedM = cachedForM Nothing
{- | Create a cached version of a function tainting result with MonadIO context.
@ -28,13 +36,13 @@ cached = cachedFor Nothing
Uses a HashMap under the hood.
-}
cachedFor :: (Monad m, MonadIO m, Hashable k, Ord k) => Maybe TimeSpec -> (k -> v) -> IO (k -> m v)
cachedFor t f =
cachedForM :: (Monad m, MonadIO m, Hashable k, Ord k) => Maybe TimeSpec -> (k -> m v) -> IO (k -> m v)
cachedForM t f =
Cache.newCache t <&> \cache k -> do
res <- liftIO $ Cache.lookup cache k
case res of
Nothing -> do
let v = f k
v <- f k
liftIO $ Cache.insert cache k v
pure v
Just v -> do

View file

@ -15,7 +15,7 @@ import Agora.Governor.Scripts (authorityTokenFromGovernor, authorityTokenSymbolF
import Agora.Proposal (Proposal)
import Agora.Proposal.Scripts (proposalPolicy, proposalValidator)
import Agora.SafeMoney (GTTag)
import Agora.ScriptInfo (PolicyInfo, ValidatorInfo, mkPolicyInfo, mkValidatorInfo)
import Agora.ScriptInfo (ScriptInfo, mkPolicyInfo, mkValidatorInfo)
import Agora.Stake (Stake)
import Agora.Stake.Scripts (stakePolicy, stakeValidator)
import Agora.Treasury (treasuryValidator)
@ -119,14 +119,14 @@ data ScriptParams where
@since 0.2.0
-}
data AgoraScripts = AgoraScripts
{ governorPolicyInfo :: PolicyInfo
, governorValidatorInfo :: ValidatorInfo
, stakePolicyInfo :: PolicyInfo
, stakeValidatorInfo :: ValidatorInfo
, proposalPolicyInfo :: PolicyInfo
, proposalValidatorInfo :: ValidatorInfo
, treasuryValidatorInfo :: ValidatorInfo
, authorityTokenPolicyInfo :: PolicyInfo
{ governorPolicyInfo :: ScriptInfo
, governorValidatorInfo :: ScriptInfo
, stakePolicyInfo :: ScriptInfo
, stakeValidatorInfo :: ScriptInfo
, proposalPolicyInfo :: ScriptInfo
, proposalValidatorInfo :: ScriptInfo
, treasuryValidatorInfo :: ScriptInfo
, authorityTokenPolicyInfo :: ScriptInfo
}
deriving anyclass
( -- | @since 0.2.0

View file

@ -11,7 +11,7 @@ module ScriptExport.API (
import Codec.Serialise.Orphans ()
import Data.Aeson qualified as Aeson
import Data.Cache.Cached (cachedFor)
import Data.Cache.Cached (cachedForM)
import Data.Function ((&))
import Data.Proxy (Proxy (Proxy))
import Data.Text (Text)
@ -35,8 +35,11 @@ import Text.Printf (printf)
@since 0.2.0
-}
type API =
"query-script" :> ReqBody '[JSON] ScriptQuery :> Post '[JSON] Aeson.Value
:<|> "info" :> Get '[JSON] ServerInfo
"query-script"
:> ReqBody '[JSON] ScriptQuery
:> Post '[JSON] Aeson.Value
:<|> "info"
:> Get '[JSON] ServerInfo
{- | Information about the server.
@ -86,7 +89,7 @@ runServer revision builders options = do
corsMiddleware = cors . const $ Just corsPolicy
-- Scripts stay cached for five minutes
query <- cachedFor (Just $ TimeSpec 300 0) (`runQuery` builders)
query <- cachedForM (Just $ TimeSpec 300 0) (`runQuery` builders)
let serverInfo =
ServerInfo

View file

@ -14,6 +14,7 @@ module ScriptExport.Types (
) where
import Data.Aeson qualified as Aeson
import Data.ByteString.Lazy.Char8 qualified as LBS
import Data.Coerce (coerce)
import Data.Default.Class (Default (def))
import Data.Hashable (Hashable)
@ -21,6 +22,7 @@ import Data.Map.Strict (Map)
import Data.Map.Strict qualified as Map
import Data.Text (Text)
import GHC.Generics qualified as GHC
import Servant qualified
{- | Query data for getting script info.
@ -55,16 +57,20 @@ data ScriptQuery = ScriptQuery
@since 0.2.0
-}
runQuery :: ScriptQuery -> Builders -> Aeson.Value
runQuery :: ScriptQuery -> Builders -> Servant.Handler Aeson.Value
runQuery s =
maybe Aeson.Null ($ s.paramsPayload) . Map.lookup s.name . getBuilders
maybe
(Servant.throwError Servant.err404 {Servant.errBody = "Builder not found"})
($ s.paramsPayload)
. Map.lookup s.name
. getBuilders
{- | Represents a list of named pure functions.
@since 0.2.0
-}
newtype Builders = Builders
{ getBuilders :: Map Text (Aeson.Value -> Aeson.Value)
{ getBuilders :: Map Text (Aeson.Value -> Servant.Handler Aeson.Value)
}
-- | @since 0.2.0
@ -84,7 +90,19 @@ insertBuilder ::
Builders
insertBuilder k = coerce . Map.insert k . throughJSON
where
throughJSON f = Aeson.toJSON . \case { Aeson.Error _ -> Nothing; Aeson.Success v -> Just (f v) } . Aeson.fromJSON
throughJSON ::
forall p s.
(Aeson.FromJSON p, Aeson.ToJSON s) =>
(p -> s) ->
(Aeson.Value -> Servant.Handler Aeson.Value)
throughJSON f v =
case Aeson.fromJSON v of
Aeson.Error e ->
Servant.throwError $
Servant.err400
{ Servant.errBody = LBS.pack e
}
Aeson.Success v' -> pure . Aeson.toJSON $ f v'
{- | Get a list of the available builders.