module Hasura.Server.Limits
  ( HasResourceLimits (..),
    ResourceLimits (..),
  )
where

import Control.Monad.Trans.Control (MonadBaseControl)
import Hasura.Base.Error
import Hasura.Prelude
import Hasura.RQL.Types.ApiLimit (ApiLimit)
import Hasura.Server.Types qualified as HGE
import Hasura.Session (UserInfo)
import Hasura.Tracing qualified as Tracing

-- | Resource limits, represented by a function which modifies IO actions to
-- enforce those limits by throwing errors using 'MonadError' in the case
-- where they are exceeded.
data ResourceLimits = ResourceLimits
  { ResourceLimits
-> forall (m :: * -> *) a.
   (MonadBaseControl IO m, MonadError QErr m) =>
   m a -> m a
runResourceLimits ::
      forall m a.
      (MonadBaseControl IO m, MonadError QErr m) =>
      m a ->
      m a
  }

-- | Monads which support resource (memory, CPU time, etc.) limiting
class (Monad m) => HasResourceLimits m where
  askHTTPHandlerLimit :: m ResourceLimits
  askGraphqlOperationLimit :: HGE.RequestId -> UserInfo -> ApiLimit -> m ResourceLimits

  -- A default for monad transformer instances
  default askHTTPHandlerLimit ::
    (m ~ t n, MonadTrans t, HasResourceLimits n) =>
    m ResourceLimits
  askHTTPHandlerLimit = n ResourceLimits -> t n ResourceLimits
forall (m :: * -> *) a. Monad m => m a -> t m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift n ResourceLimits
forall (m :: * -> *). HasResourceLimits m => m ResourceLimits
askHTTPHandlerLimit

  default askGraphqlOperationLimit ::
    (m ~ t n, MonadTrans t, HasResourceLimits n) =>
    HGE.RequestId ->
    UserInfo ->
    ApiLimit ->
    m ResourceLimits
  askGraphqlOperationLimit RequestId
reqId UserInfo
userInfo ApiLimit
apiLimit = n ResourceLimits -> t n ResourceLimits
forall (m :: * -> *) a. Monad m => m a -> t m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (n ResourceLimits -> t n ResourceLimits)
-> n ResourceLimits -> t n ResourceLimits
forall a b. (a -> b) -> a -> b
$ RequestId -> UserInfo -> ApiLimit -> n ResourceLimits
forall (m :: * -> *).
HasResourceLimits m =>
RequestId -> UserInfo -> ApiLimit -> m ResourceLimits
askGraphqlOperationLimit RequestId
reqId UserInfo
userInfo ApiLimit
apiLimit

instance (HasResourceLimits m) => HasResourceLimits (ReaderT r m)

instance (HasResourceLimits m) => HasResourceLimits (ExceptT e m)

instance (HasResourceLimits m) => HasResourceLimits (Tracing.TraceT m)