Clean AppEnv and AppContext passing, remove RunT, reduce ServerConfigCtx uses

## Description

This PR does several different things that happen to overlap; the most important being:
- it removes `RunT`: it was redundant in places where we already had `Handler`, and only used in one other place, `SchemaUpdate`, for which a local `SchemaUpdateT` is more than enough;
- it reduces the number of places where we create a `ServerConfigCtx`, since now `HasServerConfigCtx` can be implemented directly by `SchemaUpdateT` and `Handler` based on the full `AppContext`;
- it drastically reduces the number of arguments we pass around in the app init code, by introducing `HasAppEnv`;
- it simplifies `HandlerCtx` to reduce duplication

In doing so, this changes paves the way towards removing `ServerConfigCtx`, since there are only very few places where we construct it: we can now introduce smaller classes than `HasServerConfigCtx`, that expose only a relevant subset of fields, and implement them where we now implement `HasServerConfigCtx`.

This PR is loosely based on ideas in #8337, that are no longer applicable due to the changes introduced in #8159. A challenge of this PR was the postgres tests, which were running in `PGMetadataStorageAppT CacheBuild` 🙀

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8392
GitOrigin-RevId: b90c1359066d20dbea329c87762ccdd1217b4d69
This commit is contained in:
Antoine Leblanc 2023-03-21 10:44:21 +00:00 committed by hasura-bot
parent eba0a3fb33
commit 0a1628c0cc
12 changed files with 282 additions and 391 deletions

View File

@ -731,7 +731,6 @@ library
, Hasura.GraphQL.Logging.QueryLog , Hasura.GraphQL.Logging.QueryLog
, Hasura.GraphQL.Logging.ExecutionLog , Hasura.GraphQL.Logging.ExecutionLog
, Hasura.RQL.DML.Select , Hasura.RQL.DML.Select
, Hasura.RQL.Types.Run
, Hasura.Session , Hasura.Session
, Hasura.Server.API.Config , Hasura.Server.API.Config

View File

@ -294,6 +294,9 @@ newtype PGMetadataStorageAppT m a = PGMetadataStorageAppT (ReaderT AppEnv (Trace
MonadBaseControl b MonadBaseControl b
) )
instance Monad m => HasAppEnv (PGMetadataStorageAppT m) where
askAppEnv = ask
instance (MonadIO m, MonadBaseControl IO m) => MonadTrace (PGMetadataStorageAppT m) where instance (MonadIO m, MonadBaseControl IO m) => MonadTrace (PGMetadataStorageAppT m) where
newTraceWith c p n (PGMetadataStorageAppT a) = PGMetadataStorageAppT $ newTraceWith c p n a newTraceWith c p n (PGMetadataStorageAppT a) = PGMetadataStorageAppT $ newTraceWith c p n a
newSpanWith i n (PGMetadataStorageAppT a) = PGMetadataStorageAppT $ newSpanWith i n a newSpanWith i n (PGMetadataStorageAppT a) = PGMetadataStorageAppT $ newSpanWith i n a
@ -306,9 +309,6 @@ instance MonadTrans PGMetadataStorageAppT where
instance Monad m => ProvidesNetwork (PGMetadataStorageAppT m) where instance Monad m => ProvidesNetwork (PGMetadataStorageAppT m) where
askHTTPManager = asks appEnvManager askHTTPManager = asks appEnvManager
instance HasServerConfigCtx m => HasServerConfigCtx (PGMetadataStorageAppT m) where
askServerConfigCtx = lift askServerConfigCtx
runPGMetadataStorageAppT :: AppEnv -> PGMetadataStorageAppT m a -> m a runPGMetadataStorageAppT :: AppEnv -> PGMetadataStorageAppT m a -> m a
runPGMetadataStorageAppT c (PGMetadataStorageAppT a) = ignoreTraceT $ runReaderT a c runPGMetadataStorageAppT c (PGMetadataStorageAppT a) = ignoreTraceT $ runReaderT a c
@ -595,6 +595,7 @@ runHGEServer ::
LA.Forall (LA.Pure m), LA.Forall (LA.Pure m),
UserAuthentication m, UserAuthentication m,
HttpLog m, HttpLog m,
HasAppEnv m,
ConsoleRenderer m, ConsoleRenderer m,
MonadVersionAPIWithExtraData m, MonadVersionAPIWithExtraData m,
MonadMetadataApiAuthorization m, MonadMetadataApiAuthorization m,
@ -687,6 +688,7 @@ mkHGEServer ::
LA.Forall (LA.Pure m), LA.Forall (LA.Pure m),
UserAuthentication m, UserAuthentication m,
HttpLog m, HttpLog m,
HasAppEnv m,
ConsoleRenderer m, ConsoleRenderer m,
MonadVersionAPIWithExtraData m, MonadVersionAPIWithExtraData m,
MonadMetadataApiAuthorization m, MonadMetadataApiAuthorization m,
@ -757,18 +759,7 @@ mkHGEServer setupHook appStateRef appEnv@AppEnv {..} ekgStore = do
newLogTVar <- liftIO $ STM.newTVarIO False newLogTVar <- liftIO $ STM.newTVarIO False
-- Start a background thread for processing schema sync event present in the '_sscSyncEventRef' -- Start a background thread for processing schema sync event present in the '_sscSyncEventRef'
_ <- _ <- startSchemaSyncProcessorThread appStateRef newLogTVar
startSchemaSyncProcessorThread
logger
appEnvManager
appEnvMetaVersionRef
appStateRef
appEnvInstanceId
appEnvEnableMaintenanceMode
appEnvEventingMode
appEnvEnableReadOnlyMode
newLogTVar
appEnvCheckFeatureFlag
case appEnvEventingMode of case appEnvEventingMode of
EventingEnabled -> do EventingEnabled -> do

View File

@ -1,10 +1,16 @@
{-# LANGUAGE Arrows #-} {-# LANGUAGE Arrows #-}
module Hasura.App.State module Hasura.App.State
( RebuildableAppContext (..), ( -- * application state
RebuildableAppContext (..),
AppEnv (..), AppEnv (..),
AppContext (..), AppContext (..),
Loggers (..), Loggers (..),
-- * env access
HasAppEnv (..),
-- * init functions
buildRebuildableAppContext, buildRebuildableAppContext,
initSQLGenCtx, initSQLGenCtx,
) )
@ -44,6 +50,9 @@ import Network.Wai.Handler.Warp (HostPreference)
import Network.WebSockets.Connection qualified as WebSockets import Network.WebSockets.Connection qualified as WebSockets
import Refined (NonNegative, Refined) import Refined (NonNegative, Refined)
--------------------------------------------------------------------------------
-- application state
{- Note [Hasura Application State] {- Note [Hasura Application State]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Hasura Application state represents the entire state of hasura. Hasura Application state represents the entire state of hasura.
@ -151,6 +160,35 @@ data Loggers = Loggers
data InvalidationKeys = InvalidationKeys data InvalidationKeys = InvalidationKeys
--------------------------------------------------------------------------------
-- env access
-- | Provides access to the 'AppEnv'.
--
-- This class is nothing more than an equivalent of @MonadReader AppEnv m@, but
-- it abstracts it, so that application code can be written without explicitly
-- relying on an explicit implementation of the app monad. It allows for the app
-- env to be passed implicitly instead of explictly in all of the app init code.
--
-- This class is not meant to be used across the entirety of the codebase, as
-- using it brings in scope the types of all fields, creating dependencies
-- between unrelated parts of the codebase. It is only meant to be used at the
-- top level; more specific parts of the code should only rely on the relevant
-- subset of the environment, exposed by small, local typeclasses. For instance,
-- at time of writing, this can be used to implement 'HasServerConfigCtx', as a
-- first step towards breaking it down.
class Monad m => HasAppEnv m where
askAppEnv :: m AppEnv
instance (HasAppEnv m) => HasAppEnv (ReaderT r m) where
askAppEnv = lift askAppEnv
instance (HasAppEnv m) => HasAppEnv (ExceptT e m) where
askAppEnv = lift askAppEnv
--------------------------------------------------------------------------------
-- init functions
initInvalidationKeys :: InvalidationKeys initInvalidationKeys :: InvalidationKeys
initInvalidationKeys = InvalidationKeys initInvalidationKeys = InvalidationKeys

View File

@ -40,7 +40,6 @@ import Hasura.RQL.Types.Column (ColumnType, fromCol)
import Hasura.RQL.Types.Common import Hasura.RQL.Types.Common
import Hasura.RQL.Types.QueryTags (QueryTagsConfig) import Hasura.RQL.Types.QueryTags (QueryTagsConfig)
import Hasura.RQL.Types.ResultCustomization import Hasura.RQL.Types.ResultCustomization
import Hasura.RQL.Types.Run (RunT (..))
import Hasura.RQL.Types.SchemaCache.Build (MetadataT (..)) import Hasura.RQL.Types.SchemaCache.Build (MetadataT (..))
import Hasura.RemoteSchema.SchemaCache import Hasura.RemoteSchema.SchemaCache
import Hasura.SQL.AnyBackend qualified as AB import Hasura.SQL.AnyBackend qualified as AB
@ -330,6 +329,7 @@ data ExecutionStep where
-- this will need to be changed into an annotated tree. -- this will need to be changed into an annotated tree.
type ExecutionPlan = RootFieldMap ExecutionStep type ExecutionPlan = RootFieldMap ExecutionStep
-- TODO: move this to a new module.
class (Monad m) => MonadQueryTags m where class (Monad m) => MonadQueryTags m where
-- | Creates Query Tags. These are appended to the Generated SQL. -- | Creates Query Tags. These are appended to the Generated SQL.
-- Helps users to use native database monitoring tools to get some 'application-context'. -- Helps users to use native database monitoring tools to get some 'application-context'.
@ -353,6 +353,3 @@ instance (MonadQueryTags m) => MonadQueryTags (MetadataT m) where
instance (MonadQueryTags m) => MonadQueryTags (CacheRWT m) where instance (MonadQueryTags m) => MonadQueryTags (CacheRWT m) where
createQueryTags qtSourceConfig attr = retag (createQueryTags @m qtSourceConfig attr) :: Tagged (CacheRWT m) QueryTagsComment createQueryTags qtSourceConfig attr = retag (createQueryTags @m qtSourceConfig attr) :: Tagged (CacheRWT m) QueryTagsComment
instance (MonadQueryTags m) => MonadQueryTags (RunT m) where
createQueryTags qtSourceConfig attr = retag (createQueryTags @m qtSourceConfig attr) :: Tagged (RunT m) QueryTagsComment

View File

@ -1,57 +0,0 @@
{-# LANGUAGE UndecidableInstances #-}
module Hasura.RQL.Types.Run
( RunT (..),
RunCtx (..),
peelRun,
)
where
import Control.Monad.Trans
import Control.Monad.Trans.Control (MonadBaseControl)
import Hasura.Metadata.Class
import Hasura.Prelude
import Hasura.RQL.DDL.ApiLimit (MonadGetApiTimeLimit (..))
import Hasura.RQL.DDL.EventTrigger (MonadEventLogCleanup (..))
import Hasura.RQL.Types.Source
import Hasura.Server.Types
import Hasura.Services
import Hasura.Session
import Hasura.Tracing qualified as Tracing
data RunCtx = RunCtx
{ _rcUserInfo :: UserInfo,
_rcServerConfigCtx :: ServerConfigCtx
}
newtype RunT m a = RunT {unRunT :: RunCtx -> m a}
deriving
( Functor,
Applicative,
Monad,
MonadError e,
MonadIO,
Tracing.MonadTrace,
MonadBase b,
MonadBaseControl b,
MonadMetadataStorage,
MonadMetadataStorageQueryAPI,
ProvidesNetwork,
MonadResolveSource,
MonadEventLogCleanup,
MonadGetApiTimeLimit
)
via (ReaderT RunCtx m)
deriving (MonadTrans) via (ReaderT RunCtx)
instance (Monad m) => UserInfoM (RunT m) where
askUserInfo = RunT $ pure . _rcUserInfo
instance (Monad m) => HasServerConfigCtx (RunT m) where
askServerConfigCtx = RunT $ pure . _rcServerConfigCtx
peelRun ::
RunCtx ->
RunT m a ->
m a
peelRun = flip unRunT

View File

@ -18,6 +18,7 @@ import Data.Has (Has)
import Data.Text qualified as T import Data.Text qualified as T
import Data.Text.Extended qualified as T import Data.Text.Extended qualified as T
import GHC.Generics.Extended (constrName) import GHC.Generics.Extended (constrName)
import Hasura.App.State
import Hasura.Base.Error import Hasura.Base.Error
import Hasura.EncJSON import Hasura.EncJSON
import Hasura.Logging qualified as L import Hasura.Logging qualified as L
@ -66,7 +67,6 @@ import Hasura.RQL.Types.OpenTelemetry
import Hasura.RQL.Types.Permission import Hasura.RQL.Types.Permission
import Hasura.RQL.Types.QueryCollection import Hasura.RQL.Types.QueryCollection
import Hasura.RQL.Types.Roles import Hasura.RQL.Types.Roles
import Hasura.RQL.Types.Run
import Hasura.RQL.Types.ScheduledTrigger import Hasura.RQL.Types.ScheduledTrigger
import Hasura.RQL.Types.SchemaCache import Hasura.RQL.Types.SchemaCache
import Hasura.RQL.Types.SchemaCache.Build import Hasura.RQL.Types.SchemaCache.Build
@ -381,22 +381,23 @@ runMetadataQuery ::
( MonadIO m, ( MonadIO m,
MonadError QErr m, MonadError QErr m,
MonadBaseControl IO m, MonadBaseControl IO m,
HasAppEnv m,
Tracing.MonadTrace m, Tracing.MonadTrace m,
MonadMetadataStorageQueryAPI m, MonadMetadataStorageQueryAPI m,
MonadResolveSource m, MonadResolveSource m,
MonadEventLogCleanup m, MonadEventLogCleanup m,
ProvidesHasuraServices m, ProvidesHasuraServices m,
MonadGetApiTimeLimit m MonadGetApiTimeLimit m,
UserInfoM m,
HasServerConfigCtx m
) => ) =>
Env.Environment -> AppContext ->
L.Logger L.Hasura ->
InstanceId ->
UserInfo ->
ServerConfigCtx ->
RebuildableSchemaCache -> RebuildableSchemaCache ->
RQLMetadata -> RQLMetadata ->
m (EncJSON, RebuildableSchemaCache) m (EncJSON, RebuildableSchemaCache)
runMetadataQuery env logger instanceId userInfo serverConfigCtx schemaCache RQLMetadata {..} = do runMetadataQuery appContext schemaCache RQLMetadata {..} = do
AppEnv {..} <- askAppEnv
let logger = _lsLogger appEnvLoggers
(metadata, currentResourceVersion) <- Tracing.newSpan "fetchMetadata" $ liftEitherM fetchMetadata (metadata, currentResourceVersion) <- Tracing.newSpan "fetchMetadata" $ liftEitherM fetchMetadata
let exportsMetadata = \case let exportsMetadata = \case
RMV1 (RMExportMetadata _) -> True RMV1 (RMExportMetadata _) -> True
@ -420,16 +421,16 @@ runMetadataQuery env logger instanceId userInfo serverConfigCtx schemaCache RQLM
-- --
if (exportsMetadata _rqlMetadata || queryModifiesMetadata _rqlMetadata) if (exportsMetadata _rqlMetadata || queryModifiesMetadata _rqlMetadata)
then emptyMetadataDefaults then emptyMetadataDefaults
else _sccMetadataDefaults serverConfigCtx else acMetadataDefaults appContext
((r, modMetadata), modSchemaCache, cacheInvalidations) <- ((r, modMetadata), modSchemaCache, cacheInvalidations) <-
runMetadataQueryM env currentResourceVersion _rqlMetadata runMetadataQueryM (acEnvironment appContext) currentResourceVersion _rqlMetadata
-- TODO: remove this straight runReaderT that provides no actual new info
& flip runReaderT logger & flip runReaderT logger
& runMetadataT metadata metadataDefaults & runMetadataT metadata metadataDefaults
& runCacheRWT schemaCache & runCacheRWT schemaCache
& peelRun (RunCtx userInfo serverConfigCtx)
-- set modified metadata in storage -- set modified metadata in storage
if queryModifiesMetadata _rqlMetadata if queryModifiesMetadata _rqlMetadata
then case (_sccMaintenanceMode serverConfigCtx, _sccReadOnlyMode serverConfigCtx) of then case (appEnvEnableMaintenanceMode, appEnvEnableReadOnlyMode) of
(MaintenanceModeDisabled, ReadOnlyModeDisabled) -> do (MaintenanceModeDisabled, ReadOnlyModeDisabled) -> do
-- set modified metadata in storage -- set modified metadata in storage
L.unLogger logger $ L.unLogger logger $
@ -448,7 +449,7 @@ runMetadataQuery env logger instanceId userInfo serverConfigCtx schemaCache RQLM
-- notify schema cache sync -- notify schema cache sync
Tracing.newSpan "notifySchemaCacheSync" $ Tracing.newSpan "notifySchemaCacheSync" $
liftEitherM $ liftEitherM $
notifySchemaCacheSync newResourceVersion instanceId cacheInvalidations notifySchemaCacheSync newResourceVersion appEnvInstanceId cacheInvalidations
L.unLogger logger $ L.unLogger logger $
SchemaSyncLog L.LevelInfo TTMetadataApi $ SchemaSyncLog L.LevelInfo TTMetadataApi $
String $ String $
@ -458,7 +459,6 @@ runMetadataQuery env logger instanceId userInfo serverConfigCtx schemaCache RQLM
Tracing.newSpan "setMetadataResourceVersionInSchemaCache" $ Tracing.newSpan "setMetadataResourceVersionInSchemaCache" $
setMetadataResourceVersionInSchemaCache newResourceVersion setMetadataResourceVersionInSchemaCache newResourceVersion
& runCacheRWT modSchemaCache & runCacheRWT modSchemaCache
& peelRun (RunCtx userInfo serverConfigCtx)
pure (r, modSchemaCache') pure (r, modSchemaCache')
(MaintenanceModeEnabled (), ReadOnlyModeDisabled) -> (MaintenanceModeEnabled (), ReadOnlyModeDisabled) ->

View File

@ -15,6 +15,7 @@ import Data.Aeson.Casing
import Data.Aeson.TH import Data.Aeson.TH
import Data.Environment qualified as Env import Data.Environment qualified as Env
import Data.Has (Has) import Data.Has (Has)
import Hasura.App.State
import Hasura.Backends.Postgres.DDL.RunSQL import Hasura.Backends.Postgres.DDL.RunSQL
import Hasura.Base.Error import Hasura.Base.Error
import Hasura.EncJSON import Hasura.EncJSON
@ -49,7 +50,6 @@ import Hasura.RQL.Types.Endpoint
import Hasura.RQL.Types.Metadata import Hasura.RQL.Types.Metadata
import Hasura.RQL.Types.Permission import Hasura.RQL.Types.Permission
import Hasura.RQL.Types.QueryCollection import Hasura.RQL.Types.QueryCollection
import Hasura.RQL.Types.Run
import Hasura.RQL.Types.ScheduledTrigger import Hasura.RQL.Types.ScheduledTrigger
import Hasura.RQL.Types.SchemaCache.Build import Hasura.RQL.Types.SchemaCache.Build
import Hasura.RQL.Types.Source import Hasura.RQL.Types.Source
@ -177,6 +177,7 @@ $( concat
runQuery :: runQuery ::
( MonadIO m, ( MonadIO m,
MonadError QErr m, MonadError QErr m,
HasAppEnv m,
Tracing.MonadTrace m, Tracing.MonadTrace m,
MonadBaseControl IO m, MonadBaseControl IO m,
MonadMetadataStorageQueryAPI m, MonadMetadataStorageQueryAPI m,
@ -184,18 +185,18 @@ runQuery ::
MonadQueryTags m, MonadQueryTags m,
MonadEventLogCleanup m, MonadEventLogCleanup m,
ProvidesHasuraServices m, ProvidesHasuraServices m,
MonadGetApiTimeLimit m MonadGetApiTimeLimit m,
UserInfoM m,
HasServerConfigCtx m
) => ) =>
Env.Environment -> AppContext ->
L.Logger L.Hasura ->
InstanceId ->
UserInfo ->
RebuildableSchemaCache -> RebuildableSchemaCache ->
ServerConfigCtx ->
RQLQuery -> RQLQuery ->
m (EncJSON, RebuildableSchemaCache) m (EncJSON, RebuildableSchemaCache)
runQuery env logger instanceId userInfo sc serverConfigCtx query = do runQuery appContext sc query = do
when ((_sccReadOnlyMode serverConfigCtx == ReadOnlyModeEnabled) && queryModifiesUserDB query) $ AppEnv {..} <- askAppEnv
let logger = _lsLogger appEnvLoggers
when ((appEnvEnableReadOnlyMode == ReadOnlyModeEnabled) && queryModifiesUserDB query) $
throw400 NotSupported "Cannot run write queries when read-only mode is enabled" throw400 NotSupported "Cannot run write queries when read-only mode is enabled"
let exportsMetadata = \case let exportsMetadata = \case
@ -204,29 +205,22 @@ runQuery env logger instanceId userInfo sc serverConfigCtx query = do
metadataDefaults = metadataDefaults =
if (exportsMetadata query) if (exportsMetadata query)
then emptyMetadataDefaults then emptyMetadataDefaults
else _sccMetadataDefaults serverConfigCtx else acMetadataDefaults appContext
(metadata, currentResourceVersion) <- liftEitherM fetchMetadata (metadata, currentResourceVersion) <- liftEitherM fetchMetadata
result <- ((result, updatedMetadata), updatedCache, invalidations) <-
runReaderT (runQueryM env query) logger & \x -> do runQueryM (acEnvironment appContext) query
((js, meta), rsc, ci) <- -- TODO: remove this straight runReaderT that provides no actual new info
x & flip runReaderT logger
& runMetadataT metadata metadataDefaults & runMetadataT metadata metadataDefaults
& runCacheRWT sc & runCacheRWT sc
& peelRun runCtx
pure (js, rsc, ci, meta)
withReload currentResourceVersion result
where
runCtx = RunCtx userInfo serverConfigCtx
withReload currentResourceVersion (result, updatedCache, invalidations, updatedMetadata) = do
when (queryModifiesSchemaCache query) $ do when (queryModifiesSchemaCache query) $ do
case (_sccMaintenanceMode serverConfigCtx) of case appEnvEnableMaintenanceMode of
MaintenanceModeDisabled -> do MaintenanceModeDisabled -> do
-- set modified metadata in storage -- set modified metadata in storage
newResourceVersion <- liftEitherM $ setMetadata currentResourceVersion updatedMetadata newResourceVersion <- liftEitherM $ setMetadata currentResourceVersion updatedMetadata
-- notify schema cache sync -- notify schema cache sync
liftEitherM $ notifySchemaCacheSync newResourceVersion instanceId invalidations liftEitherM $ notifySchemaCacheSync newResourceVersion appEnvInstanceId invalidations
MaintenanceModeEnabled () -> MaintenanceModeEnabled () ->
throw500 "metadata cannot be modified in maintenance mode" throw500 "metadata cannot be modified in maintenance mode"
pure (result, updatedCache) pure (result, updatedCache)

View File

@ -16,6 +16,7 @@ import Data.Aeson.Types (Parser)
import Data.Environment qualified as Env import Data.Environment qualified as Env
import Data.Text qualified as T import Data.Text qualified as T
import GHC.Generics.Extended (constrName) import GHC.Generics.Extended (constrName)
import Hasura.App.State
import Hasura.Backends.BigQuery.DDL.RunSQL qualified as BigQuery import Hasura.Backends.BigQuery.DDL.RunSQL qualified as BigQuery
import Hasura.Backends.DataConnector.Adapter.RunSQL qualified as DataConnector import Hasura.Backends.DataConnector.Adapter.RunSQL qualified as DataConnector
import Hasura.Backends.DataConnector.Adapter.Types (DataConnectorName, mkDataConnectorName) import Hasura.Backends.DataConnector.Adapter.Types (DataConnectorName, mkDataConnectorName)
@ -41,7 +42,6 @@ import Hasura.RQL.DML.Types
) )
import Hasura.RQL.DML.Update import Hasura.RQL.DML.Update
import Hasura.RQL.Types.Metadata import Hasura.RQL.Types.Metadata
import Hasura.RQL.Types.Run
import Hasura.RQL.Types.SchemaCache.Build import Hasura.RQL.Types.SchemaCache.Build
import Hasura.RQL.Types.Source import Hasura.RQL.Types.Source
import Hasura.SQL.Backend import Hasura.SQL.Backend
@ -105,40 +105,32 @@ runQuery ::
( MonadIO m, ( MonadIO m,
MonadBaseControl IO m, MonadBaseControl IO m,
MonadError QErr m, MonadError QErr m,
HasAppEnv m,
Tracing.MonadTrace m, Tracing.MonadTrace m,
MonadMetadataStorage m, MonadMetadataStorage m,
MonadResolveSource m, MonadResolveSource m,
MonadQueryTags m, MonadQueryTags m,
ProvidesHasuraServices m ProvidesHasuraServices m,
UserInfoM m,
HasServerConfigCtx m
) => ) =>
Env.Environment -> AppContext ->
InstanceId ->
UserInfo ->
RebuildableSchemaCache -> RebuildableSchemaCache ->
ServerConfigCtx ->
RQLQuery -> RQLQuery ->
m (EncJSON, RebuildableSchemaCache) m (EncJSON, RebuildableSchemaCache)
runQuery env instanceId userInfo schemaCache serverConfigCtx rqlQuery = do runQuery appContext schemaCache rqlQuery = do
when ((_sccReadOnlyMode serverConfigCtx == ReadOnlyModeEnabled) && queryModifiesUserDB rqlQuery) $ AppEnv {..} <- askAppEnv
when ((appEnvEnableReadOnlyMode == ReadOnlyModeEnabled) && queryModifiesUserDB rqlQuery) $
throw400 NotSupported "Cannot run write queries when read-only mode is enabled" throw400 NotSupported "Cannot run write queries when read-only mode is enabled"
(metadata, currentResourceVersion) <- Tracing.newSpan "fetchMetadata" $ liftEitherM fetchMetadata (metadata, currentResourceVersion) <- Tracing.newSpan "fetchMetadata" $ liftEitherM fetchMetadata
result <- ((result, updatedMetadata), updatedCache, invalidations) <-
runQueryM env rqlQuery & \x -> do runQueryM (acEnvironment appContext) rqlQuery
((js, meta), rsc, ci) <-
-- We can use defaults here unconditionally, since there is no MD export function in V2Query -- We can use defaults here unconditionally, since there is no MD export function in V2Query
x & runMetadataT metadata (acMetadataDefaults appContext)
& runMetadataT metadata (_sccMetadataDefaults serverConfigCtx)
& runCacheRWT schemaCache & runCacheRWT schemaCache
& peelRun runCtx
pure (js, rsc, ci, meta)
withReload currentResourceVersion result
where
runCtx = RunCtx userInfo serverConfigCtx
withReload currentResourceVersion (result, updatedCache, invalidations, updatedMetadata) = do
when (queryModifiesSchema rqlQuery) $ do when (queryModifiesSchema rqlQuery) $ do
case _sccMaintenanceMode serverConfigCtx of case appEnvEnableMaintenanceMode of
MaintenanceModeDisabled -> do MaintenanceModeDisabled -> do
-- set modified metadata in storage -- set modified metadata in storage
newResourceVersion <- newResourceVersion <-
@ -148,7 +140,7 @@ runQuery env instanceId userInfo schemaCache serverConfigCtx rqlQuery = do
-- notify schema cache sync -- notify schema cache sync
Tracing.newSpan "notifySchemaCacheSync" $ Tracing.newSpan "notifySchemaCacheSync" $
liftEitherM $ liftEitherM $
notifySchemaCacheSync newResourceVersion instanceId invalidations notifySchemaCacheSync newResourceVersion appEnvInstanceId invalidations
MaintenanceModeEnabled () -> MaintenanceModeEnabled () ->
throw500 "metadata cannot be modified in maintenance mode" throw500 "metadata cannot be modified in maintenance mode"
pure (result, updatedCache) pure (result, updatedCache)

View File

@ -6,7 +6,7 @@ module Hasura.Server.App
ConsoleRenderer (..), ConsoleRenderer (..),
MonadVersionAPIWithExtraData (..), MonadVersionAPIWithExtraData (..),
Handler, Handler,
HandlerCtx (hcReqHeaders, hcAppContext, hcSchemaCache, hcAppEnv, hcUser), HandlerCtx (hcReqHeaders, hcAppContext, hcSchemaCache, hcUser),
HasuraApp (HasuraApp), HasuraApp (HasuraApp),
Loggers (..), Loggers (..),
MonadConfigApiHandler (..), MonadConfigApiHandler (..),
@ -113,14 +113,13 @@ import Web.Spock.Core ((<//>))
import Web.Spock.Core qualified as Spock import Web.Spock.Core qualified as Spock
data HandlerCtx = HandlerCtx data HandlerCtx = HandlerCtx
{ hcAppContext :: !AppContext, { hcAppContext :: AppContext,
hcSchemaCache :: !RebuildableSchemaCache, hcSchemaCache :: RebuildableSchemaCache,
hcSchemaCacheVersion :: !SchemaCacheVer, hcSchemaCacheVersion :: SchemaCacheVer,
hcAppEnv :: !AppEnv, hcUser :: UserInfo,
hcUser :: !UserInfo, hcReqHeaders :: [HTTP.Header],
hcReqHeaders :: ![HTTP.Header], hcRequestId :: RequestId,
hcRequestId :: !RequestId, hcSourceIpAddress :: Wai.IpAddress
hcSourceIpAddress :: !Wai.IpAddress
} }
newtype Handler m a = Handler (ReaderT HandlerCtx (ExceptT QErr m) a) newtype Handler m a = Handler (ReaderT HandlerCtx (ExceptT QErr m) a)
@ -135,9 +134,9 @@ newtype Handler m a = Handler (ReaderT HandlerCtx (ExceptT QErr m) a)
MonadReader HandlerCtx, MonadReader HandlerCtx,
MonadError QErr, MonadError QErr,
MonadTrace, MonadTrace,
HasAppEnv,
HasResourceLimits, HasResourceLimits,
MonadResolveSource, MonadResolveSource,
HasServerConfigCtx,
E.MonadGQLExecutionCheck, E.MonadGQLExecutionCheck,
MonadEventLogCleanup, MonadEventLogCleanup,
MonadQueryLog, MonadQueryLog,
@ -154,6 +153,28 @@ newtype Handler m a = Handler (ReaderT HandlerCtx (ExceptT QErr m) a)
instance MonadTrans Handler where instance MonadTrans Handler where
lift = Handler . lift . lift lift = Handler . lift . lift
instance Monad m => UserInfoM (Handler m) where
askUserInfo = asks hcUser
instance (HasAppEnv m) => HasServerConfigCtx (Handler m) where
askServerConfigCtx = Handler do
AppEnv {..} <- askAppEnv
AppContext {..} <- asks hcAppContext
pure
ServerConfigCtx
{ _sccFunctionPermsCtx = acFunctionPermsCtx,
_sccRemoteSchemaPermsCtx = acRemoteSchemaPermsCtx,
_sccSQLGenCtx = acSQLGenCtx,
_sccMaintenanceMode = appEnvEnableMaintenanceMode,
_sccExperimentalFeatures = acExperimentalFeatures,
_sccEventingMode = appEnvEventingMode,
_sccReadOnlyMode = appEnvEnableReadOnlyMode,
_sccDefaultNamingConvention = acDefaultNamingConvention,
_sccMetadataDefaults = acMetadataDefaults,
_sccCheckFeatureFlag = appEnvCheckFeatureFlag,
_sccApolloFederationStatus = acApolloFederationStatus
}
runHandler :: (HasResourceLimits m, MonadBaseControl IO m) => HandlerCtx -> Handler m a -> m (Either QErr a) runHandler :: (HasResourceLimits m, MonadBaseControl IO m) => HandlerCtx -> Handler m a -> m (Either QErr a)
runHandler ctx (Handler r) = do runHandler ctx (Handler r) = do
handlerLimit <- askHTTPHandlerLimit handlerLimit <- askHTTPHandlerLimit
@ -268,6 +289,7 @@ mkSpockAction ::
forall m a impl. forall m a impl.
( MonadIO m, ( MonadIO m,
MonadBaseControl IO m, MonadBaseControl IO m,
HasAppEnv m,
FromJSON a, FromJSON a,
UserAuthentication m, UserAuthentication m,
HttpLog m, HttpLog m,
@ -275,20 +297,20 @@ mkSpockAction ::
MonadTrace m MonadTrace m
) => ) =>
AppStateRef impl -> AppStateRef impl ->
AppEnv ->
-- | `QErr` JSON encoder function -- | `QErr` JSON encoder function
(Bool -> QErr -> Value) -> (Bool -> QErr -> Value) ->
-- | `QErr` modifier -- | `QErr` modifier
(QErr -> QErr) -> (QErr -> QErr) ->
APIHandler m a -> APIHandler m a ->
Spock.ActionT m () Spock.ActionT m ()
mkSpockAction appStateRef appEnv@AppEnv {..} qErrEncoder qErrModifier apiHandler = do mkSpockAction appStateRef qErrEncoder qErrModifier apiHandler = do
AppEnv {..} <- lift askAppEnv
AppContext {..} <- liftIO $ getAppContext appStateRef
req <- Spock.request req <- Spock.request
let origHeaders = Wai.requestHeaders req let origHeaders = Wai.requestHeaders req
ipAddress = Wai.getSourceFromFallback req ipAddress = Wai.getSourceFromFallback req
pathInfo = Wai.rawPathInfo req pathInfo = Wai.rawPathInfo req
AppContext {..} <- liftIO $ getAppContext appStateRef
-- Bytes are actually read from the socket here. Time this. -- Bytes are actually read from the socket here. Time this.
(ioWaitTime, reqBody) <- withElapsedTime $ liftIO $ Wai.strictRequestBody req (ioWaitTime, reqBody) <- withElapsedTime $ liftIO $ Wai.strictRequestBody req
@ -330,7 +352,7 @@ mkSpockAction appStateRef appEnv@AppEnv {..} qErrEncoder qErrModifier apiHandler
pure pure
( userInfo, ( userInfo,
authHeaders, authHeaders,
HandlerCtx appContext schemaCache schemaCacheVer appEnv userInfo headers requestId ipAddress, HandlerCtx appContext schemaCache schemaCacheVer userInfo headers requestId ipAddress,
shouldIncludeInternal (_uiRole userInfo) acResponseInternalErrorsConfig, shouldIncludeInternal (_uiRole userInfo) acResponseInternalErrorsConfig,
extraUserInfo extraUserInfo
) )
@ -378,8 +400,7 @@ mkSpockAction appStateRef appEnv@AppEnv {..} qErrEncoder qErrModifier apiHandler
logSuccessAndResp (Just userInfo) requestId req (reqBody, queryJSON) res (Just (ioWaitTime, serviceTime)) origHeaders authHeaders httpLogMetadata logSuccessAndResp (Just userInfo) requestId req (reqBody, queryJSON) res (Just (ioWaitTime, serviceTime)) origHeaders authHeaders httpLogMetadata
where where
logErrorAndResp :: logErrorAndResp ::
forall n a3 ctx. forall any ctx.
(MonadIO n, HttpLog n) =>
Maybe UserInfo -> Maybe UserInfo ->
RequestId -> RequestId ->
Wai.Request -> Wai.Request ->
@ -388,9 +409,10 @@ mkSpockAction appStateRef appEnv@AppEnv {..} qErrEncoder qErrModifier apiHandler
[HTTP.Header] -> [HTTP.Header] ->
ExtraUserInfo -> ExtraUserInfo ->
QErr -> QErr ->
Spock.ActionCtxT ctx n a3 Spock.ActionCtxT ctx m any
logErrorAndResp userInfo reqId waiReq req includeInternal headers extraUserInfo qErr = do logErrorAndResp userInfo reqId waiReq req includeInternal headers extraUserInfo qErr = do
let httpLogMetadata = buildHttpLogMetadata @n emptyHttpLogGraphQLInfo extraUserInfo AppEnv {..} <- lift askAppEnv
let httpLogMetadata = buildHttpLogMetadata @m emptyHttpLogGraphQLInfo extraUserInfo
jsonResponse = J.encode $ qErrEncoder includeInternal qErr jsonResponse = J.encode $ qErrEncoder includeInternal qErr
contentLength = ("Content-Length", B8.toStrict $ BB.toLazyByteString $ BB.int64Dec $ BL.length jsonResponse) contentLength = ("Content-Length", B8.toStrict $ BB.toLazyByteString $ BB.int64Dec $ BL.length jsonResponse)
allHeaders = [contentLength, jsonHeader] allHeaders = [contentLength, jsonHeader]
@ -400,6 +422,7 @@ mkSpockAction appStateRef appEnv@AppEnv {..} qErrEncoder qErrModifier apiHandler
Spock.lazyBytes jsonResponse Spock.lazyBytes jsonResponse
logSuccessAndResp userInfo reqId waiReq req result qTime reqHeaders authHdrs httpLoggingMetadata = do logSuccessAndResp userInfo reqId waiReq req result qTime reqHeaders authHdrs httpLoggingMetadata = do
AppEnv {..} <- lift askAppEnv
let (respBytes, respHeaders) = case result of let (respBytes, respHeaders) = case result of
JSONResp (HttpResponse encJson h) -> (encJToLBS encJson, pure jsonHeader <> h) JSONResp (HttpResponse encJson h) -> (encJToLBS encJson, pure jsonHeader <> h)
RawResp (HttpResponse rawBytes h) -> (rawBytes, h) RawResp (HttpResponse rawBytes h) -> (rawBytes, h)
@ -431,49 +454,29 @@ v1QueryHandler ::
MonadReader HandlerCtx m, MonadReader HandlerCtx m,
MonadMetadataStorageQueryAPI m, MonadMetadataStorageQueryAPI m,
MonadResolveSource m, MonadResolveSource m,
HasAppEnv m,
EB.MonadQueryTags m, EB.MonadQueryTags m,
MonadEventLogCleanup m, MonadEventLogCleanup m,
ProvidesNetwork m, ProvidesNetwork m,
MonadGetApiTimeLimit m MonadGetApiTimeLimit m,
UserInfoM m,
HasServerConfigCtx m
) => ) =>
AppStateRef impl -> AppStateRef impl ->
RQLQuery -> RQLQuery ->
m (HttpResponse EncJSON) m (HttpResponse EncJSON)
v1QueryHandler appStateRef query = do v1QueryHandler appStateRef query = do
(liftEitherM . authorizeV1QueryApi query) =<< ask (liftEitherM . authorizeV1QueryApi query) =<< ask
logger <- asks (_lsLogger . appEnvLoggers . hcAppEnv) logger <- _lsLogger . appEnvLoggers <$> askAppEnv
res <- bool (fst <$> (action logger)) (withSchemaCacheUpdate appStateRef logger Nothing (action logger)) $ queryModifiesSchemaCache query res <- bool (fst <$> action) (withSchemaCacheUpdate appStateRef logger Nothing action) $ queryModifiesSchemaCache query
return $ HttpResponse res [] return $ HttpResponse res []
where where
action logger = do action = do
userInfo <- asks hcUser appContext <- asks hcAppContext
AppContext {..} <- asks hcAppContext
schemaCache <- asks hcSchemaCache schemaCache <- asks hcSchemaCache
instanceId <- asks (appEnvInstanceId . hcAppEnv)
maintenanceMode <- asks (appEnvEnableMaintenanceMode . hcAppEnv)
eventingMode <- asks (appEnvEventingMode . hcAppEnv)
readOnlyMode <- asks (appEnvEnableReadOnlyMode . hcAppEnv)
checkFeatureFlag <- asks (appEnvCheckFeatureFlag . hcAppEnv)
let serverConfigCtx =
ServerConfigCtx
acFunctionPermsCtx
acRemoteSchemaPermsCtx
acSQLGenCtx
maintenanceMode
acExperimentalFeatures
eventingMode
readOnlyMode
acDefaultNamingConvention
acMetadataDefaults
checkFeatureFlag
acApolloFederationStatus
runQuery runQuery
acEnvironment appContext
logger
instanceId
userInfo
schemaCache schemaCache
serverConfigCtx
query query
-- | See Note [Explicitly passing AppStateRef] -- | See Note [Explicitly passing AppStateRef]
@ -487,47 +490,27 @@ v1MetadataHandler ::
MonadResolveSource m, MonadResolveSource m,
MonadMetadataApiAuthorization m, MonadMetadataApiAuthorization m,
MonadEventLogCleanup m, MonadEventLogCleanup m,
HasAppEnv m,
ProvidesNetwork m, ProvidesNetwork m,
MonadGetApiTimeLimit m MonadGetApiTimeLimit m,
UserInfoM m,
HasServerConfigCtx m
) => ) =>
AppStateRef impl -> AppStateRef impl ->
RQLMetadata -> RQLMetadata ->
m (HttpResponse EncJSON) m (HttpResponse EncJSON)
v1MetadataHandler appStateRef query = Tracing.newSpan "Metadata" $ do v1MetadataHandler appStateRef query = Tracing.newSpan "Metadata" $ do
(liftEitherM . authorizeV1MetadataApi query) =<< ask (liftEitherM . authorizeV1MetadataApi query) =<< ask
userInfo <- asks hcUser logger <- _lsLogger . appEnvLoggers <$> askAppEnv
AppContext {..} <- asks hcAppContext appContext <- asks hcAppContext
schemaCache <- asks hcSchemaCache schemaCache <- asks hcSchemaCache
instanceId <- asks (appEnvInstanceId . hcAppEnv)
logger <- asks (_lsLogger . appEnvLoggers . hcAppEnv)
maintenanceMode <- asks (appEnvEnableMaintenanceMode . hcAppEnv)
eventingMode <- asks (appEnvEventingMode . hcAppEnv)
readOnlyMode <- asks (appEnvEnableReadOnlyMode . hcAppEnv)
checkFeatureFlag <- asks (appEnvCheckFeatureFlag . hcAppEnv)
let serverConfigCtx =
ServerConfigCtx
acFunctionPermsCtx
acRemoteSchemaPermsCtx
acSQLGenCtx
maintenanceMode
acExperimentalFeatures
eventingMode
readOnlyMode
acDefaultNamingConvention
acMetadataDefaults
checkFeatureFlag
acApolloFederationStatus
r <- r <-
withSchemaCacheUpdate withSchemaCacheUpdate
appStateRef appStateRef
logger logger
Nothing Nothing
$ runMetadataQuery $ runMetadataQuery
acEnvironment appContext
logger
instanceId
userInfo
serverConfigCtx
schemaCache schemaCache
query query
pure $ HttpResponse r [] pure $ HttpResponse r []
@ -541,15 +524,18 @@ v2QueryHandler ::
MonadReader HandlerCtx m, MonadReader HandlerCtx m,
MonadMetadataStorage m, MonadMetadataStorage m,
MonadResolveSource m, MonadResolveSource m,
HasAppEnv m,
EB.MonadQueryTags m, EB.MonadQueryTags m,
ProvidesNetwork m ProvidesNetwork m,
UserInfoM m,
HasServerConfigCtx m
) => ) =>
AppStateRef impl -> AppStateRef impl ->
V2Q.RQLQuery -> V2Q.RQLQuery ->
m (HttpResponse EncJSON) m (HttpResponse EncJSON)
v2QueryHandler appStateRef query = Tracing.newSpan "v2 Query" $ do v2QueryHandler appStateRef query = Tracing.newSpan "v2 Query" $ do
(liftEitherM . authorizeV2QueryApi query) =<< ask (liftEitherM . authorizeV2QueryApi query) =<< ask
logger <- asks (_lsLogger . appEnvLoggers . hcAppEnv) logger <- _lsLogger . appEnvLoggers <$> askAppEnv
res <- res <-
bool (fst <$> dbAction) (withSchemaCacheUpdate appStateRef logger Nothing dbAction) $ bool (fst <$> dbAction) (withSchemaCacheUpdate appStateRef logger Nothing dbAction) $
V2Q.queryModifiesSchema query V2Q.queryModifiesSchema query
@ -557,29 +543,12 @@ v2QueryHandler appStateRef query = Tracing.newSpan "v2 Query" $ do
where where
-- Hit postgres -- Hit postgres
dbAction = do dbAction = do
userInfo <- asks hcUser
AppContext {..} <- asks hcAppContext
schemaCache <- asks hcSchemaCache schemaCache <- asks hcSchemaCache
instanceId <- asks (appEnvInstanceId . hcAppEnv) appContext <- asks hcAppContext
maintenanceMode <- asks (appEnvEnableMaintenanceMode . hcAppEnv) V2Q.runQuery
eventingMode <- asks (appEnvEventingMode . hcAppEnv) appContext
readOnlyMode <- asks (appEnvEnableReadOnlyMode . hcAppEnv) schemaCache
checkFeatureFlag <- asks (appEnvCheckFeatureFlag . hcAppEnv) query
let serverConfigCtx =
ServerConfigCtx
acFunctionPermsCtx
acRemoteSchemaPermsCtx
acSQLGenCtx
maintenanceMode
acExperimentalFeatures
eventingMode
readOnlyMode
acDefaultNamingConvention
acMetadataDefaults
checkFeatureFlag
acApolloFederationStatus
V2Q.runQuery acEnvironment instanceId userInfo schemaCache serverConfigCtx query
v1Alpha1GQHandler :: v1Alpha1GQHandler ::
( MonadIO m, ( MonadIO m,
@ -588,6 +557,7 @@ v1Alpha1GQHandler ::
MonadQueryLog m, MonadQueryLog m,
MonadExecutionLog m, MonadExecutionLog m,
MonadTrace m, MonadTrace m,
HasAppEnv m,
GH.MonadExecuteQuery m, GH.MonadExecuteQuery m,
MonadError QErr m, MonadError QErr m,
MonadReader HandlerCtx m, MonadReader HandlerCtx m,
@ -600,9 +570,9 @@ v1Alpha1GQHandler ::
GH.GQLBatchedReqs (GH.GQLReq GH.GQLQueryText) -> GH.GQLBatchedReqs (GH.GQLReq GH.GQLQueryText) ->
m (HttpLogGraphQLInfo, HttpResponse EncJSON) m (HttpLogGraphQLInfo, HttpResponse EncJSON)
v1Alpha1GQHandler queryType query = do v1Alpha1GQHandler queryType query = do
userInfo <- asks hcUser appEnv <- askAppEnv
appCtx@AppContext {..} <- asks hcAppContext appCtx@AppContext {..} <- asks hcAppContext
appEnv <- asks hcAppEnv userInfo <- asks hcUser
schemaCache <- asks hcSchemaCache schemaCache <- asks hcSchemaCache
schemaCacheVer <- asks hcSchemaCacheVersion schemaCacheVer <- asks hcSchemaCacheVersion
reqHeaders <- asks hcReqHeaders reqHeaders <- asks hcReqHeaders
@ -638,6 +608,7 @@ v1GQHandler ::
MonadQueryLog m, MonadQueryLog m,
MonadExecutionLog m, MonadExecutionLog m,
MonadTrace m, MonadTrace m,
HasAppEnv m,
GH.MonadExecuteQuery m, GH.MonadExecuteQuery m,
MonadError QErr m, MonadError QErr m,
MonadReader HandlerCtx m, MonadReader HandlerCtx m,
@ -657,6 +628,7 @@ v1GQRelayHandler ::
MonadQueryLog m, MonadQueryLog m,
MonadExecutionLog m, MonadExecutionLog m,
MonadTrace m, MonadTrace m,
HasAppEnv m,
GH.MonadExecuteQuery m, GH.MonadExecuteQuery m,
MonadError QErr m, MonadError QErr m,
MonadReader HandlerCtx m, MonadReader HandlerCtx m,
@ -755,6 +727,7 @@ configApiGetHandler ::
forall m impl. forall m impl.
( MonadIO m, ( MonadIO m,
MonadBaseControl IO m, MonadBaseControl IO m,
HasAppEnv m,
UserAuthentication m, UserAuthentication m,
HttpLog m, HttpLog m,
HasResourceLimits m, HasResourceLimits m,
@ -763,10 +736,10 @@ configApiGetHandler ::
AppStateRef impl -> AppStateRef impl ->
AppEnv -> AppEnv ->
Spock.SpockCtxT () m () Spock.SpockCtxT () m ()
configApiGetHandler appStateRef appEnv = do configApiGetHandler appStateRef AppEnv {..} = do
AppContext {..} <- liftIO $ getAppContext appStateRef AppContext {..} <- liftIO $ getAppContext appStateRef
Spock.get "v1alpha1/config" $ Spock.get "v1alpha1/config" $
mkSpockAction appStateRef appEnv encodeQErr id $ mkSpockAction appStateRef encodeQErr id $
mkGetHandler $ do mkGetHandler $ do
onlyAdmin onlyAdmin
let res = let res =
@ -777,7 +750,7 @@ configApiGetHandler appStateRef appEnv = do
acEnableAllowlist acEnableAllowlist
acLiveQueryOptions acLiveQueryOptions
acStreamQueryOptions acStreamQueryOptions
(appEnvConsoleAssetsDir appEnv) appEnvConsoleAssetsDir
acExperimentalFeatures acExperimentalFeatures
acEnabledAPIs acEnabledAPIs
acDefaultNamingConvention acDefaultNamingConvention
@ -798,6 +771,7 @@ mkWaiApp ::
ConsoleRenderer m, ConsoleRenderer m,
MonadVersionAPIWithExtraData m, MonadVersionAPIWithExtraData m,
HttpLog m, HttpLog m,
HasAppEnv m,
UserAuthentication m, UserAuthentication m,
MonadMetadataApiAuthorization m, MonadMetadataApiAuthorization m,
E.MonadGQLExecutionCheck m, E.MonadGQLExecutionCheck m,
@ -849,6 +823,7 @@ httpApp ::
ConsoleRenderer m, ConsoleRenderer m,
MonadVersionAPIWithExtraData m, MonadVersionAPIWithExtraData m,
HttpLog m, HttpLog m,
HasAppEnv m,
UserAuthentication m, UserAuthentication m,
MonadMetadataApiAuthorization m, MonadMetadataApiAuthorization m,
E.MonadGQLExecutionCheck m, E.MonadGQLExecutionCheck m,
@ -932,25 +907,11 @@ httpApp setupHook appStateRef appEnv@AppEnv {..} ekgStore = do
Spock.lazyBytes $ encode $ object $ ["version" .= currentVersion] <> extraData Spock.lazyBytes $ encode $ object $ ["version" .= currentVersion] <> extraData
let customEndpointHandler :: let customEndpointHandler ::
forall n.
( MonadIO n,
MonadBaseControl IO n,
E.MonadGQLExecutionCheck n,
MonadQueryLog n,
MonadExecutionLog n,
GH.MonadExecuteQuery n,
MonadMetadataStorage n,
EB.MonadQueryTags n,
HasResourceLimits n,
ProvidesNetwork n,
MonadTrace n
) =>
RestRequest Spock.SpockMethod -> RestRequest Spock.SpockMethod ->
Handler n (HttpLogGraphQLInfo, APIResp) Handler m (HttpLogGraphQLInfo, APIResp)
customEndpointHandler restReq = do customEndpointHandler restReq = do
endpoints <- liftIO $ scEndpoints <$> getSchemaCache appStateRef endpoints <- liftIO $ scEndpoints <$> getSchemaCache appStateRef
appCtx' <- asks hcAppContext appCtx' <- asks hcAppContext
appEnv' <- asks hcAppEnv
schemaCache <- asks hcSchemaCache schemaCache <- asks hcSchemaCache
schemaCacheVer <- asks hcSchemaCacheVersion schemaCacheVer <- asks hcSchemaCacheVersion
requestId <- asks hcRequestId requestId <- asks hcRequestId
@ -958,7 +919,7 @@ httpApp setupHook appStateRef appEnv@AppEnv {..} ekgStore = do
reqHeaders <- asks hcReqHeaders reqHeaders <- asks hcReqHeaders
ipAddress <- asks hcSourceIpAddress ipAddress <- asks hcSourceIpAddress
let execCtx = mkExecutionContext appCtx' appEnv' schemaCache schemaCacheVer let execCtx = mkExecutionContext appCtx' appEnv schemaCache schemaCacheVer
req <- req <-
restReq & traverse \case restReq & traverse \case
@ -1113,20 +1074,13 @@ httpApp setupHook appStateRef appEnv@AppEnv {..} ekgStore = do
logHttpError logger appEnvLoggingSettings Nothing reqId req (reqBody, Nothing) err headers (emptyHttpLogMetadata @m) logHttpError logger appEnvLoggingSettings Nothing reqId req (reqBody, Nothing) err headers (emptyHttpLogMetadata @m)
spockAction :: spockAction ::
forall a n. forall a.
( FromJSON a, (FromJSON a) =>
MonadIO n,
MonadBaseControl IO n,
UserAuthentication n,
HttpLog n,
MonadTrace n,
HasResourceLimits n
) =>
(Bool -> QErr -> Value) -> (Bool -> QErr -> Value) ->
(QErr -> QErr) -> (QErr -> QErr) ->
APIHandler n a -> APIHandler m a ->
Spock.ActionT n () Spock.ActionT m ()
spockAction qErrEncoder qErrModifier apiHandler = mkSpockAction appStateRef appEnv qErrEncoder qErrModifier apiHandler spockAction qErrEncoder qErrModifier apiHandler = mkSpockAction appStateRef qErrEncoder qErrModifier apiHandler
-- all graphql errors should be of type 200 -- all graphql errors should be of type 200
allMod200 qe = qe {qeStatus = HTTP.status200} allMod200 qe = qe {qeStatus = HTTP.status200}

View File

@ -27,7 +27,6 @@ import Hasura.Metadata.Class
import Hasura.Prelude import Hasura.Prelude
import Hasura.RQL.DDL.Schema (runCacheRWT) import Hasura.RQL.DDL.Schema (runCacheRWT)
import Hasura.RQL.DDL.Schema.Catalog import Hasura.RQL.DDL.Schema.Catalog
import Hasura.RQL.Types.Run
import Hasura.RQL.Types.SchemaCache import Hasura.RQL.Types.SchemaCache
import Hasura.RQL.Types.SchemaCache.Build import Hasura.RQL.Types.SchemaCache.Build
import Hasura.RQL.Types.Source import Hasura.RQL.Types.Source
@ -39,12 +38,10 @@ import Hasura.Server.AppStateRef
readSchemaCacheRef, readSchemaCacheRef,
withSchemaCacheUpdate, withSchemaCacheUpdate,
) )
import Hasura.Server.Init (FeatureFlag)
import Hasura.Server.Logging import Hasura.Server.Logging
import Hasura.Server.Types import Hasura.Server.Types
import Hasura.Services import Hasura.Services
import Hasura.Session import Hasura.Session
import Network.HTTP.Client qualified as HTTP
import Refined (NonNegative, Refined, unrefine) import Refined (NonNegative, Refined, unrefine)
data ThreadError data ThreadError
@ -141,37 +138,22 @@ startSchemaSyncListenerThread logger pool instanceId interval metaVersionRef = d
-- See Note [Schema Cache Sync] -- See Note [Schema Cache Sync]
startSchemaSyncProcessorThread :: startSchemaSyncProcessorThread ::
( C.ForkableMonadIO m, ( C.ForkableMonadIO m,
HasAppEnv m,
MonadMetadataStorage m, MonadMetadataStorage m,
MonadResolveSource m, MonadResolveSource m,
ProvidesNetwork m ProvidesNetwork m
) => ) =>
Logger Hasura ->
HTTP.Manager ->
STM.TMVar MetadataResourceVersion ->
AppStateRef impl -> AppStateRef impl ->
InstanceId ->
(MaintenanceMode ()) ->
EventingMode ->
ReadOnlyMode ->
STM.TVar Bool -> STM.TVar Bool ->
(FeatureFlag -> IO Bool) ->
ManagedT m Immortal.Thread ManagedT m Immortal.Thread
startSchemaSyncProcessorThread startSchemaSyncProcessorThread appStateRef logTVar = do
logger AppEnv {..} <- lift askAppEnv
httpMgr let logger = _lsLogger appEnvLoggers
schemaSyncEventRef
appStateRef
instanceId
maintenanceMode
eventingMode
readOnlyMode
logTVar
checkFeatureFlag = do
-- Start processor thread -- Start processor thread
processorThread <- processorThread <-
C.forkManagedT "SchemeUpdate.processor" logger $ C.forkManagedT "SchemeUpdate.processor" logger $
processor logger httpMgr schemaSyncEventRef appStateRef instanceId maintenanceMode eventingMode readOnlyMode logTVar checkFeatureFlag processor appEnvMetadataVersionRef appStateRef logTVar
logThreadStarted logger instanceId TTProcessor processorThread logThreadStarted logger appEnvInstanceId TTProcessor processorThread
pure processorThread pure processorThread
-- TODO: This is also defined in multitenant, consider putting it in a library somewhere -- TODO: This is also defined in multitenant, consider putting it in a library somewhere
@ -261,76 +243,85 @@ listener logger pool metaVersionRef interval = L.iterateM_ listenerLoop defaultE
processor :: processor ::
forall m void impl. forall m void impl.
( C.ForkableMonadIO m, ( C.ForkableMonadIO m,
HasAppEnv m,
MonadMetadataStorage m, MonadMetadataStorage m,
MonadResolveSource m, MonadResolveSource m,
ProvidesNetwork m ProvidesNetwork m
) => ) =>
Logger Hasura ->
HTTP.Manager ->
STM.TMVar MetadataResourceVersion -> STM.TMVar MetadataResourceVersion ->
AppStateRef impl -> AppStateRef impl ->
InstanceId ->
(MaintenanceMode ()) ->
EventingMode ->
ReadOnlyMode ->
STM.TVar Bool -> STM.TVar Bool ->
(FeatureFlag -> IO Bool) ->
m void m void
processor processor
logger
_httpMgr
metaVersionRef metaVersionRef
appStateRef appStateRef
instanceId logTVar = forever do
maintenanceMode
eventingMode
readOnlyMode
logTVar
checkFeatureFlag = forever $ do
metaVersion <- liftIO $ STM.atomically $ STM.takeTMVar metaVersionRef metaVersion <- liftIO $ STM.atomically $ STM.takeTMVar metaVersionRef
AppContext {..} <- liftIO $ getAppContext appStateRef refreshSchemaCache metaVersion appStateRef TTProcessor logTVar
let serverConfigCtx =
newtype SchemaUpdateT m a = SchemaUpdateT (AppContext -> m a)
deriving
( Functor,
Applicative,
Monad,
MonadError e,
MonadIO,
MonadMetadataStorage,
ProvidesNetwork,
MonadResolveSource
)
via (ReaderT AppContext m)
deriving (MonadTrans) via (ReaderT AppContext)
runSchemaUpdate :: AppContext -> SchemaUpdateT m a -> m a
runSchemaUpdate appContext (SchemaUpdateT action) = action appContext
instance (Monad m) => UserInfoM (SchemaUpdateT m) where
askUserInfo = pure adminUserInfo
instance (HasAppEnv m) => HasServerConfigCtx (SchemaUpdateT m) where
askServerConfigCtx = SchemaUpdateT \AppContext {..} -> do
AppEnv {..} <- askAppEnv
pure
ServerConfigCtx ServerConfigCtx
acFunctionPermsCtx { _sccFunctionPermsCtx = acFunctionPermsCtx,
acRemoteSchemaPermsCtx _sccRemoteSchemaPermsCtx = acRemoteSchemaPermsCtx,
acSQLGenCtx _sccSQLGenCtx = acSQLGenCtx,
maintenanceMode _sccMaintenanceMode = appEnvEnableMaintenanceMode,
acExperimentalFeatures _sccExperimentalFeatures = acExperimentalFeatures,
eventingMode _sccEventingMode = appEnvEventingMode,
readOnlyMode _sccReadOnlyMode = appEnvEnableReadOnlyMode,
acDefaultNamingConvention _sccDefaultNamingConvention = acDefaultNamingConvention,
acMetadataDefaults _sccMetadataDefaults = acMetadataDefaults,
checkFeatureFlag _sccCheckFeatureFlag = appEnvCheckFeatureFlag,
acApolloFederationStatus _sccApolloFederationStatus = acApolloFederationStatus
refreshSchemaCache metaVersion instanceId logger appStateRef TTProcessor serverConfigCtx logTVar }
refreshSchemaCache :: refreshSchemaCache ::
( MonadIO m, ( MonadIO m,
MonadBaseControl IO m, MonadBaseControl IO m,
HasAppEnv m,
MonadMetadataStorage m, MonadMetadataStorage m,
MonadResolveSource m, MonadResolveSource m,
ProvidesNetwork m ProvidesNetwork m
) => ) =>
MetadataResourceVersion -> MetadataResourceVersion ->
InstanceId ->
Logger Hasura ->
AppStateRef impl -> AppStateRef impl ->
SchemaSyncThreadType -> SchemaSyncThreadType ->
ServerConfigCtx ->
STM.TVar Bool -> STM.TVar Bool ->
m () m ()
refreshSchemaCache refreshSchemaCache
resourceVersion resourceVersion
instanceId
logger
appStateRef appStateRef
threadType threadType
serverConfigCtx
logTVar = do logTVar = do
AppEnv {..} <- askAppEnv
let logger = _lsLogger appEnvLoggers
respErr <- runExceptT $ respErr <- runExceptT $
withSchemaCacheUpdate appStateRef logger (Just logTVar) $ do withSchemaCacheUpdate appStateRef logger (Just logTVar) $ do
rebuildableCache <- liftIO $ fst <$> readSchemaCacheRef appStateRef rebuildableCache <- liftIO $ fst <$> readSchemaCacheRef appStateRef
(msg, cache, _) <- peelRun runCtx $ appContext <- liftIO $ getAppContext appStateRef
(msg, cache, _) <- runSchemaUpdate appContext $
runCacheRWT rebuildableCache $ do runCacheRWT rebuildableCache $ do
schemaCache <- askSchemaCache schemaCache <- askSchemaCache
case scMetadataResourceVersion schemaCache of case scMetadataResourceVersion schemaCache of
@ -360,7 +351,7 @@ refreshSchemaCache
"Fetched metadata with resource version " "Fetched metadata with resource version "
<> tshow latestResourceVersion <> tshow latestResourceVersion
notifications <- liftEitherM $ fetchMetadataNotifications engineResourceVersion instanceId notifications <- liftEitherM $ fetchMetadataNotifications engineResourceVersion appEnvInstanceId
case notifications of case notifications of
[] -> do [] -> do
@ -395,8 +386,6 @@ refreshSchemaCache
logInfo logger threadType $ object ["message" .= ("Schema Version changed with notifications" :: Text)] logInfo logger threadType $ object ["message" .= ("Schema Version changed with notifications" :: Text)]
pure (msg, cache) pure (msg, cache)
onLeft respErr (logError logger threadType . TEQueryError) onLeft respErr (logError logger threadType . TEQueryError)
where
runCtx = RunCtx adminUserInfo serverConfigCtx
logInfo :: (MonadIO m) => Logger Hasura -> SchemaSyncThreadType -> Value -> m () logInfo :: (MonadIO m) => Logger Hasura -> SchemaSyncThreadType -> Value -> m ()
logInfo logger threadType val = logInfo logger threadType val =
@ -408,10 +397,3 @@ logError logger threadType err =
unLogger logger $ unLogger logger $
SchemaSyncLog LevelError threadType $ SchemaSyncLog LevelError threadType $
object ["error" .= toJSON err] object ["error" .= toJSON err]
-- Currently unused
_logDebug :: (MonadIO m) => Logger Hasura -> SchemaSyncThreadType -> String -> m ()
_logDebug logger threadType msg =
unLogger logger $
SchemaSyncLog LevelDebug threadType $
object ["message" .= msg]

View File

@ -131,25 +131,22 @@ main = do
) )
$ \(appStateRef, appEnv) -> return (appStateRef, appEnv) $ \(appStateRef, appEnv) -> return (appStateRef, appEnv)
let run :: ExceptT QErr (PGMetadataStorageAppT CacheBuild) a -> IO a let run :: ExceptT QErr (PGMetadataStorageAppT IO) a -> IO a
run = run =
runExceptT runExceptT
>>> runPGMetadataStorageAppT appEnv >>> runPGMetadataStorageAppT appEnv
>>> runCacheBuild cacheBuildParams >>> flip onLeftM printErrJExit
>>> runExceptT
>=> flip onLeft printErrJExit
>=> flip onLeft printErrJExit
(metadata, schemaCache) <- run do (metadata, schemaCache) <- run do
metadata <- metadata <-
snd snd
<$> (liftEitherM . runExceptT . _pecRunTx pgContext (PGExecCtxInfo (Tx PG.ReadWrite Nothing) InternalRawQuery)) <$> (liftEitherM . runExceptT . _pecRunTx pgContext (PGExecCtxInfo (Tx PG.ReadWrite Nothing) InternalRawQuery))
(migrateCatalog (Just sourceConfig) defaultPostgresExtensionsSchema maintenanceMode =<< liftIO getCurrentTime) (migrateCatalog (Just sourceConfig) defaultPostgresExtensionsSchema maintenanceMode =<< liftIO getCurrentTime)
schemaCache <- lift $ lift $ buildRebuildableSchemaCache logger envMap metadata schemaCache <- runCacheBuild cacheBuildParams $ buildRebuildableSchemaCache logger envMap metadata
pure (metadata, schemaCache) pure (metadata, schemaCache)
cacheRef <- newMVar schemaCache cacheRef <- newMVar schemaCache
pure $ NT (run . flip MigrateSuite.runCacheRefT cacheRef . fmap fst . runMetadataT metadata emptyMetadataDefaults) pure $ NT (run . flip MigrateSuite.runCacheRefT (serverConfigCtx, cacheRef) . fmap fst . runMetadataT metadata emptyMetadataDefaults)
streamingSubscriptionSuite <- StreamingSubscriptionSuite.buildStreamingSubscriptionSuite streamingSubscriptionSuite <- StreamingSubscriptionSuite.buildStreamingSubscriptionSuite
eventTriggerLogCleanupSuite <- EventTriggerCleanupSuite.buildEventTriggerCleanupSuite eventTriggerLogCleanupSuite <- EventTriggerCleanupSuite.buildEventTriggerCleanupSuite

View File

@ -38,7 +38,7 @@ import Test.Hspec.Expectations.Lifted
-- -- NOTE: downgrade test disabled for now (see #5273) -- -- NOTE: downgrade test disabled for now (see #5273)
newtype CacheRefT m a = CacheRefT {runCacheRefT :: MVar RebuildableSchemaCache -> m a} newtype CacheRefT m a = CacheRefT {runCacheRefT :: (ServerConfigCtx, MVar RebuildableSchemaCache) -> m a}
deriving deriving
( Functor, ( Functor,
Applicative, Applicative,
@ -47,15 +47,19 @@ newtype CacheRefT m a = CacheRefT {runCacheRefT :: MVar RebuildableSchemaCache -
MonadError e, MonadError e,
MonadBase b, MonadBase b,
MonadBaseControl b, MonadBaseControl b,
MonadReader (ServerConfigCtx, MVar RebuildableSchemaCache),
MonadTx, MonadTx,
UserInfoM, UserInfoM,
HasServerConfigCtx,
MonadMetadataStorage, MonadMetadataStorage,
MonadMetadataStorageQueryAPI, MonadMetadataStorageQueryAPI,
MonadResolveSource,
ProvidesNetwork, ProvidesNetwork,
MonadGetApiTimeLimit MonadGetApiTimeLimit
) )
via (ReaderT (MVar RebuildableSchemaCache) m) via (ReaderT (ServerConfigCtx, MVar RebuildableSchemaCache) m)
instance Monad m => HasServerConfigCtx (CacheRefT m) where
askServerConfigCtx = asks fst
instance MonadTrans CacheRefT where instance MonadTrans CacheRefT where
lift = CacheRefT . const lift = CacheRefT . const
@ -65,7 +69,7 @@ instance MFunctor CacheRefT where
-- instance (MonadBase IO m) => TableCoreInfoRM 'Postgres (CacheRefT m) -- instance (MonadBase IO m) => TableCoreInfoRM 'Postgres (CacheRefT m)
instance (MonadBase IO m) => CacheRM (CacheRefT m) where instance (MonadBase IO m) => CacheRM (CacheRefT m) where
askSchemaCache = CacheRefT (fmap lastBuiltSchemaCache . readMVar) askSchemaCache = CacheRefT (fmap lastBuiltSchemaCache . readMVar . snd)
instance (MonadEventLogCleanup m) => MonadEventLogCleanup (CacheRefT m) where instance (MonadEventLogCleanup m) => MonadEventLogCleanup (CacheRefT m) where
runLogCleaner conf = lift $ runLogCleaner conf runLogCleaner conf = lift $ runLogCleaner conf
@ -77,18 +81,19 @@ instance
MonadBaseControl IO m, MonadBaseControl IO m,
MonadError QErr m, MonadError QErr m,
MonadResolveSource m, MonadResolveSource m,
HasServerConfigCtx m,
ProvidesNetwork m ProvidesNetwork m
) => ) =>
CacheRWM (CacheRefT m) CacheRWM (CacheRefT m)
where where
buildSchemaCacheWithOptions reason invalidations metadata = buildSchemaCacheWithOptions reason invalidations metadata = do
CacheRefT $ flip modifyMVar \schemaCache -> do scVar <- asks snd
modifyMVar scVar \schemaCache -> do
((), cache, _) <- runCacheRWT schemaCache (buildSchemaCacheWithOptions reason invalidations metadata) ((), cache, _) <- runCacheRWT schemaCache (buildSchemaCacheWithOptions reason invalidations metadata)
pure (cache, ()) pure (cache, ())
setMetadataResourceVersionInSchemaCache resourceVersion = setMetadataResourceVersionInSchemaCache resourceVersion = do
CacheRefT $ flip modifyMVar \schemaCache -> do scVar <- asks snd
modifyMVar scVar \schemaCache -> do
((), cache, _) <- runCacheRWT schemaCache (setMetadataResourceVersionInSchemaCache resourceVersion) ((), cache, _) <- runCacheRWT schemaCache (setMetadataResourceVersionInSchemaCache resourceVersion)
pure (cache, ()) pure (cache, ())
@ -106,7 +111,6 @@ suite ::
( MonadIO m, ( MonadIO m,
MonadError QErr m, MonadError QErr m,
MonadBaseControl IO m, MonadBaseControl IO m,
HasServerConfigCtx m,
MonadResolveSource m, MonadResolveSource m,
MonadMetadataStorageQueryAPI m, MonadMetadataStorageQueryAPI m,
MonadEventLogCleanup m, MonadEventLogCleanup m,
@ -127,9 +131,9 @@ suite srcConfig pgExecCtx pgConnInfo = do
(migrationResult, metadata) <- runTx' pgExecCtx $ migrateCatalog (Just srcConfig) (ExtensionsSchema "public") MaintenanceModeDisabled time (migrationResult, metadata) <- runTx' pgExecCtx $ migrateCatalog (Just srcConfig) (ExtensionsSchema "public") MaintenanceModeDisabled time
(,migrationResult) <$> runCacheBuildM (buildRebuildableSchemaCache logger env metadata) (,migrationResult) <$> runCacheBuildM (buildRebuildableSchemaCache logger env metadata)
dropAndInit env time = lift $ dropAndInit env time = lift do
CacheRefT $ flip modifyMVar \_ -> scVar <- asks snd
(runTx' pgExecCtx dropHdbCatalogSchema) *> (migrateCatalogAndBuildCache env time) modifyMVar scVar $ const $ (runTx' pgExecCtx dropHdbCatalogSchema) *> (migrateCatalogAndBuildCache env time)
downgradeTo v = runTx' pgExecCtx . downgradeCatalog (Just srcConfig) DowngradeOptions {dgoDryRun = False, dgoTargetVersion = v} downgradeTo v = runTx' pgExecCtx . downgradeCatalog (Just srcConfig) DowngradeOptions {dgoDryRun = False, dgoTargetVersion = v}
describe "migrateCatalog" $ do describe "migrateCatalog" $ do
@ -149,9 +153,9 @@ suite srcConfig pgExecCtx pgConnInfo = do
secondDump `shouldBe` firstDump secondDump `shouldBe` firstDump
it "supports upgrades after downgrade to version 12" \(NT transact) -> do it "supports upgrades after downgrade to version 12" \(NT transact) -> do
let upgradeToLatest env time = lift $ let upgradeToLatest env time = lift do
CacheRefT $ flip modifyMVar \_ -> scVar <- asks snd
migrateCatalogAndBuildCache env time modifyMVar scVar $ const $ migrateCatalogAndBuildCache env time
env <- Env.getEnvironment env <- Env.getEnvironment
time <- getCurrentTime time <- getCurrentTime
transact (dropAndInit env time) `shouldReturn` MRInitialized transact (dropAndInit env time) `shouldReturn` MRInitialized