mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-09-20 15:09:02 +03:00
server: disable caching for actions with forward client headers enabled
Co-authored-by: Lyndon Maydwell <92299+sordina@users.noreply.github.com> Co-authored-by: Antoine Leblanc <1618949+nicuveo@users.noreply.github.com> Co-authored-by: Abby Sassel <3883855+sassela@users.noreply.github.com> Co-authored-by: hasura-bot <30118761+hasura-bot@users.noreply.github.com> Co-authored-by: Rikin Kachhia <54616969+rikinsk@users.noreply.github.com> Co-authored-by: Ikechukwu Eze <22247592+iykekings@users.noreply.github.com> Co-authored-by: Aleksandra Sikora <9019397+beerose@users.noreply.github.com> Co-authored-by: Rishichandra Wawhal <27274869+wawhal@users.noreply.github.com> Co-authored-by: Naveen Naidu <30195193+Naveenaidu@users.noreply.github.com> Co-authored-by: Vishnu Bharathi <4211715+scriptnull@users.noreply.github.com> GitOrigin-RevId: c9a8be3cb607f7767e9d6791717106adf123e3a8
This commit is contained in:
parent
17dc201fef
commit
b274a2d240
@ -134,6 +134,7 @@ query {
|
|||||||
- server: fix action custom types failing to parse when mutually recursive
|
- server: fix action custom types failing to parse when mutually recursive
|
||||||
- server: fix MSSQL table name descriptions
|
- server: fix MSSQL table name descriptions
|
||||||
- server: emit `postgres-max-connections-error` when max postgres connections are reached
|
- server: emit `postgres-max-connections-error` when max postgres connections are reached
|
||||||
|
- server: disable caching for actions when "forward-client-headers" option is turned on
|
||||||
- console: allow editing rest endpoints queries and misc ui improvements
|
- console: allow editing rest endpoints queries and misc ui improvements
|
||||||
- console: display collection names and queries from all collections in allowlist
|
- console: display collection names and queries from all collections in allowlist
|
||||||
- cli: match ordering of keys in project metadata files with server metadata
|
- cli: match ordering of keys in project metadata files with server metadata
|
||||||
|
@ -23,8 +23,9 @@ used) cache, and removed from the cache as needed based on usage.
|
|||||||
|
|
||||||
A query's response can be cached only if the following conditions hold:
|
A query's response can be cached only if the following conditions hold:
|
||||||
|
|
||||||
- The query does not make use of remote schemas or remote joins
|
- The query does **not** make use of ``remote schemas`` or ``remote joins``.
|
||||||
- The response JSON is under 100KB in size
|
- The query **isn't** an ``Action`` that has ``forward_client_headers`` (see :ref:`ActionDefinition`) set to ``true``.
|
||||||
|
- The response JSON is **under** 100KB in size
|
||||||
|
|
||||||
.. admonition:: Support
|
.. admonition:: Support
|
||||||
|
|
||||||
|
@ -721,7 +721,7 @@ instance HttpLog PGMetadataStorageApp where
|
|||||||
mkHttpAccessLogContext userInfoM reqId waiReq compressedResponse qTime cType headers
|
mkHttpAccessLogContext userInfoM reqId waiReq compressedResponse qTime cType headers
|
||||||
|
|
||||||
instance MonadExecuteQuery PGMetadataStorageApp where
|
instance MonadExecuteQuery PGMetadataStorageApp where
|
||||||
cacheLookup _ _ = pure ([], Nothing)
|
cacheLookup _ _ _ = pure ([], Nothing)
|
||||||
cacheStore _ _ = pure ()
|
cacheStore _ _ = pure ()
|
||||||
|
|
||||||
instance UserAuthentication (Tracing.TraceT PGMetadataStorageApp) where
|
instance UserAuthentication (Tracing.TraceT PGMetadataStorageApp) where
|
||||||
|
@ -185,7 +185,7 @@ resolveActionMutationAsync
|
|||||||
resolveActionMutationAsync annAction reqHeaders sessionVariables =
|
resolveActionMutationAsync annAction reqHeaders sessionVariables =
|
||||||
insertAction actionName sessionVariables reqHeaders inputArgs
|
insertAction actionName sessionVariables reqHeaders inputArgs
|
||||||
where
|
where
|
||||||
AnnActionMutationAsync actionName inputArgs = annAction
|
AnnActionMutationAsync actionName _ inputArgs = annAction
|
||||||
|
|
||||||
{- Note: [Resolving async action query]
|
{- Note: [Resolving async action query]
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -260,7 +260,7 @@ resolveAsyncActionQuery userInfo annAction =
|
|||||||
|
|
||||||
in RS.AnnSelectG annotatedFields tableFromExp tablePermissions tableArguments stringifyNumerics
|
in RS.AnnSelectG annotatedFields tableFromExp tablePermissions tableArguments stringifyNumerics
|
||||||
where
|
where
|
||||||
AnnActionAsyncQuery _ actionId outputType asyncFields definitionList stringifyNumerics actionSource = annAction
|
AnnActionAsyncQuery _ actionId outputType asyncFields definitionList stringifyNumerics _ actionSource = annAction
|
||||||
|
|
||||||
idColumn = (unsafePGCol "id", PGUUID)
|
idColumn = (unsafePGCol "id", PGUUID)
|
||||||
responsePayloadColumn = (unsafePGCol "response_payload", PGJSONB)
|
responsePayloadColumn = (unsafePGCol "response_payload", PGJSONB)
|
||||||
|
@ -23,6 +23,7 @@ import Hasura.GraphQL.Execute.Action.Types (ActionExecutionPlan)
|
|||||||
import Hasura.GraphQL.Execute.LiveQuery.Plan
|
import Hasura.GraphQL.Execute.LiveQuery.Plan
|
||||||
import Hasura.GraphQL.Parser hiding (Type)
|
import Hasura.GraphQL.Parser hiding (Type)
|
||||||
import Hasura.RQL.IR.RemoteJoin
|
import Hasura.RQL.IR.RemoteJoin
|
||||||
|
import Hasura.RQL.Types.Action
|
||||||
import Hasura.RQL.Types.Backend
|
import Hasura.RQL.Types.Backend
|
||||||
import Hasura.RQL.Types.Common
|
import Hasura.RQL.Types.Common
|
||||||
import Hasura.RQL.Types.Error
|
import Hasura.RQL.Types.Error
|
||||||
@ -100,7 +101,7 @@ data ExecutionStep where
|
|||||||
-> ExecutionStep
|
-> ExecutionStep
|
||||||
-- ^ A query to execute against the database
|
-- ^ A query to execute against the database
|
||||||
ExecStepAction
|
ExecStepAction
|
||||||
:: ActionExecutionPlan
|
:: (ActionExecutionPlan, ActionsInfo)
|
||||||
-> ExecutionStep
|
-> ExecutionStep
|
||||||
-- ^ Execute an action
|
-- ^ Execute an action
|
||||||
ExecStepRemote
|
ExecStepRemote
|
||||||
|
@ -89,11 +89,15 @@ convertMutationSelectionSet env logger gqlContext SQLGenCtx{stringifyNum} userIn
|
|||||||
RFRemote remoteField -> do
|
RFRemote remoteField -> do
|
||||||
RemoteFieldG remoteSchemaInfo resolvedRemoteField <- resolveRemoteField userInfo remoteField
|
RemoteFieldG remoteSchemaInfo resolvedRemoteField <- resolveRemoteField userInfo remoteField
|
||||||
pure $ buildExecStepRemote remoteSchemaInfo G.OperationTypeMutation $ [G.SelectionField resolvedRemoteField]
|
pure $ buildExecStepRemote remoteSchemaInfo G.OperationTypeMutation $ [G.SelectionField resolvedRemoteField]
|
||||||
RFAction action ->
|
RFAction action -> do
|
||||||
ExecStepAction <$> convertMutationAction env logger userInfo manager reqHeaders action
|
(actionName, _fch) <- pure $ case action of
|
||||||
|
AMSync s -> (_aaeName s, _aaeForwardClientHeaders s)
|
||||||
|
AMAsync s -> (_aamaName s, _aamaForwardClientHeaders s)
|
||||||
|
plan <- convertMutationAction env logger userInfo manager reqHeaders action
|
||||||
|
pure $ ExecStepAction (plan, ActionsInfo actionName _fch) -- `_fch` represents the `forward_client_headers` option from the action
|
||||||
|
-- definition which is currently being ignored for actions that are mutations
|
||||||
RFRaw s ->
|
RFRaw s ->
|
||||||
pure $ ExecStepRaw s
|
pure $ ExecStepRaw s
|
||||||
|
|
||||||
return (txs, resolvedSelSet)
|
return (txs, resolvedSelSet)
|
||||||
where
|
where
|
||||||
reportParseErrors errs = case NE.head errs of
|
reportParseErrors errs = case NE.head errs of
|
||||||
|
@ -86,10 +86,11 @@ convertQuerySelSet env logger gqlContext userInfo manager reqHeaders directives
|
|||||||
RFRemote rf -> do
|
RFRemote rf -> do
|
||||||
RemoteFieldG remoteSchemaInfo remoteField <- for rf $ resolveRemoteVariable userInfo
|
RemoteFieldG remoteSchemaInfo remoteField <- for rf $ resolveRemoteVariable userInfo
|
||||||
pure $ buildExecStepRemote remoteSchemaInfo G.OperationTypeQuery [G.SelectionField remoteField]
|
pure $ buildExecStepRemote remoteSchemaInfo G.OperationTypeQuery [G.SelectionField remoteField]
|
||||||
RFAction a ->
|
RFAction a -> do
|
||||||
pure $ ExecStepAction $ case a of
|
(action, actionName, fch) <- pure $ case a of
|
||||||
AQQuery s -> AEPSync $ resolveActionExecution env logger userInfo s (ActionExecContext manager reqHeaders usrVars)
|
AQQuery s -> (AEPSync $ resolveActionExecution env logger userInfo s (ActionExecContext manager reqHeaders usrVars), _aaeName s, _aaeForwardClientHeaders s)
|
||||||
AQAsync s -> AEPAsyncQuery $ AsyncActionQueryExecutionPlan (_aaaqActionId s) $ resolveAsyncActionQuery userInfo s
|
AQAsync s -> (AEPAsyncQuery $ AsyncActionQueryExecutionPlan (_aaaqActionId s) $ resolveAsyncActionQuery userInfo s, _aaaqName s, _aaaqForwardClientHeaders s)
|
||||||
|
pure $ ExecStepAction (action, (ActionsInfo actionName fch))
|
||||||
RFRaw r ->
|
RFRaw r ->
|
||||||
pure $ ExecStepRaw r
|
pure $ ExecStepRaw r
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ actionExecute
|
|||||||
-> m (Maybe (FieldParser n (AnnActionExecution 'Postgres (UnpreparedValue 'Postgres))))
|
-> m (Maybe (FieldParser n (AnnActionExecution 'Postgres (UnpreparedValue 'Postgres))))
|
||||||
actionExecute nonObjectTypeMap actionInfo = runMaybeT do
|
actionExecute nonObjectTypeMap actionInfo = runMaybeT do
|
||||||
roleName <- askRoleName
|
roleName <- askRoleName
|
||||||
guard $ (roleName == adminRoleName || roleName `Map.member` permissions)
|
guard (roleName == adminRoleName || roleName `Map.member` permissions)
|
||||||
let fieldName = unActionName actionName
|
let fieldName = unActionName actionName
|
||||||
description = G.Description <$> comment
|
description = G.Description <$> comment
|
||||||
inputArguments <- lift $ actionInputArguments nonObjectTypeMap $ _adArguments definition
|
inputArguments <- lift $ actionInputArguments nonObjectTypeMap $ _adArguments definition
|
||||||
@ -77,7 +77,7 @@ actionExecute nonObjectTypeMap actionInfo = runMaybeT do
|
|||||||
, _aaeSource = getActionSourceInfo outputObject
|
, _aaeSource = getActionSourceInfo outputObject
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
ActionInfo actionName (outputType, outputObject) definition permissions comment = actionInfo
|
ActionInfo actionName (outputType, outputObject) definition permissions _ comment = actionInfo
|
||||||
|
|
||||||
-- | actionAsyncMutation is used to execute a asynchronous mutation action. An
|
-- | actionAsyncMutation is used to execute a asynchronous mutation action. An
|
||||||
-- asynchronous action expects the field name and the input arguments to the
|
-- asynchronous action expects the field name and the input arguments to the
|
||||||
@ -97,9 +97,9 @@ actionAsyncMutation nonObjectTypeMap actionInfo = runMaybeT do
|
|||||||
let fieldName = unActionName actionName
|
let fieldName = unActionName actionName
|
||||||
description = G.Description <$> comment
|
description = G.Description <$> comment
|
||||||
pure $ P.selection fieldName description inputArguments actionIdParser
|
pure $ P.selection fieldName description inputArguments actionIdParser
|
||||||
<&> AnnActionMutationAsync actionName
|
<&> AnnActionMutationAsync actionName forwardClientHeaders
|
||||||
where
|
where
|
||||||
ActionInfo actionName _ definition permissions comment = actionInfo
|
ActionInfo actionName _ definition permissions forwardClientHeaders comment = actionInfo
|
||||||
|
|
||||||
-- | actionAsyncQuery is used to query/subscribe to the result of an
|
-- | actionAsyncQuery is used to query/subscribe to the result of an
|
||||||
-- asynchronous mutation action. The only input argument to an
|
-- asynchronous mutation action. The only input argument to an
|
||||||
@ -165,10 +165,11 @@ actionAsyncQuery actionInfo = runMaybeT do
|
|||||||
, _aaaqFields = fields
|
, _aaaqFields = fields
|
||||||
, _aaaqDefinitionList = mkDefinitionList outputObject
|
, _aaaqDefinitionList = mkDefinitionList outputObject
|
||||||
, _aaaqStringifyNum = stringifyNum
|
, _aaaqStringifyNum = stringifyNum
|
||||||
|
, _aaaqForwardClientHeaders = forwardClientHeaders
|
||||||
, _aaaqSource = getActionSourceInfo outputObject
|
, _aaaqSource = getActionSourceInfo outputObject
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
ActionInfo actionName (outputType, outputObject) definition permissions comment = actionInfo
|
ActionInfo actionName (outputType, outputObject) definition permissions forwardClientHeaders comment = actionInfo
|
||||||
idFieldName = $$(G.litName "id")
|
idFieldName = $$(G.litName "id")
|
||||||
idFieldDescription = "the unique id of an action"
|
idFieldDescription = "the unique id of an action"
|
||||||
|
|
||||||
|
@ -86,6 +86,8 @@ class Monad m => MonadExecuteQuery m where
|
|||||||
cacheLookup
|
cacheLookup
|
||||||
:: [RemoteSchemaInfo]
|
:: [RemoteSchemaInfo]
|
||||||
-- ^ Used to check if the elaborated query supports caching
|
-- ^ Used to check if the elaborated query supports caching
|
||||||
|
-> [ActionsInfo]
|
||||||
|
-- ^ Used to check if actions query supports caching (unsupported if `forward_client_headers` is set)
|
||||||
-> QueryCacheKey
|
-> QueryCacheKey
|
||||||
-- ^ Key that uniquely identifies the result of a query execution
|
-- ^ Key that uniquely identifies the result of a query execution
|
||||||
-> TraceT (ExceptT QErr m) (HTTP.ResponseHeaders, Maybe EncJSON)
|
-> TraceT (ExceptT QErr m) (HTTP.ResponseHeaders, Maybe EncJSON)
|
||||||
@ -112,19 +114,19 @@ class Monad m => MonadExecuteQuery m where
|
|||||||
-- ^ Always succeeds
|
-- ^ Always succeeds
|
||||||
|
|
||||||
instance MonadExecuteQuery m => MonadExecuteQuery (ReaderT r m) where
|
instance MonadExecuteQuery m => MonadExecuteQuery (ReaderT r m) where
|
||||||
cacheLookup a b = hoist (hoist lift) $ cacheLookup a b
|
cacheLookup a b c = hoist (hoist lift) $ cacheLookup a b c
|
||||||
cacheStore a b = hoist (hoist lift) $ cacheStore a b
|
cacheStore a b = hoist (hoist lift) $ cacheStore a b
|
||||||
|
|
||||||
instance MonadExecuteQuery m => MonadExecuteQuery (ExceptT r m) where
|
instance MonadExecuteQuery m => MonadExecuteQuery (ExceptT r m) where
|
||||||
cacheLookup a b = hoist (hoist lift) $ cacheLookup a b
|
cacheLookup a b c = hoist (hoist lift) $ cacheLookup a b c
|
||||||
cacheStore a b = hoist (hoist lift) $ cacheStore a b
|
cacheStore a b = hoist (hoist lift) $ cacheStore a b
|
||||||
|
|
||||||
instance MonadExecuteQuery m => MonadExecuteQuery (TraceT m) where
|
instance MonadExecuteQuery m => MonadExecuteQuery (TraceT m) where
|
||||||
cacheLookup a b = hoist (hoist lift) $ cacheLookup a b
|
cacheLookup a b c = hoist (hoist lift) $ cacheLookup a b c
|
||||||
cacheStore a b = hoist (hoist lift) $ cacheStore a b
|
cacheStore a b = hoist (hoist lift) $ cacheStore a b
|
||||||
|
|
||||||
instance MonadExecuteQuery m => MonadExecuteQuery (MetadataStorageT m) where
|
instance MonadExecuteQuery m => MonadExecuteQuery (MetadataStorageT m) where
|
||||||
cacheLookup a b = hoist (hoist lift) $ cacheLookup a b
|
cacheLookup a b c = hoist (hoist lift) $ cacheLookup a b c
|
||||||
cacheStore a b = hoist (hoist lift) $ cacheStore a b
|
cacheStore a b = hoist (hoist lift) $ cacheStore a b
|
||||||
|
|
||||||
-- | A partial result, e.g. from a remote schema or postgres, which we'll
|
-- | A partial result, e.g. from a remote schema or postgres, which we'll
|
||||||
@ -222,7 +224,12 @@ runGQ env logger reqId userInfo ipAddress reqHeaders queryType reqUnparsed = do
|
|||||||
E.ExecStepDB _headers exists ->
|
E.ExecStepDB _headers exists ->
|
||||||
AB.dispatchAnyBackend @BackendTransport exists EB.getRemoteSchemaInfo
|
AB.dispatchAnyBackend @BackendTransport exists EB.getRemoteSchemaInfo
|
||||||
_ -> []
|
_ -> []
|
||||||
(responseHeaders, cachedValue) <- Tracing.interpTraceT (liftEitherM . runExceptT) $ cacheLookup remoteJoins cacheKey
|
actionsInfo = foldl getExecStepActionWithActionInfo [] $ OMap.elems $ OMap.filter (\x -> case x of
|
||||||
|
E.ExecStepAction (_, _) -> True
|
||||||
|
_ -> False
|
||||||
|
) queryPlans
|
||||||
|
|
||||||
|
(responseHeaders, cachedValue) <- Tracing.interpTraceT (liftEitherM . runExceptT) $ cacheLookup remoteJoins actionsInfo cacheKey
|
||||||
case fmap decodeGQResp cachedValue of
|
case fmap decodeGQResp cachedValue of
|
||||||
Just cachedResponseData ->
|
Just cachedResponseData ->
|
||||||
pure (Telem.Query, 0, Telem.Local, HttpResponse cachedResponseData responseHeaders, normalizedSelectionSet)
|
pure (Telem.Query, 0, Telem.Local, HttpResponse cachedResponseData responseHeaders, normalizedSelectionSet)
|
||||||
@ -244,7 +251,7 @@ runGQ env logger reqId userInfo ipAddress reqHeaders queryType reqUnparsed = do
|
|||||||
return $ ResultsFragment telemTimeIO_DT Telem.Local resp []
|
return $ ResultsFragment telemTimeIO_DT Telem.Local resp []
|
||||||
E.ExecStepRemote rsi gqlReq ->
|
E.ExecStepRemote rsi gqlReq ->
|
||||||
runRemoteGQ httpManager fieldName rsi gqlReq
|
runRemoteGQ httpManager fieldName rsi gqlReq
|
||||||
E.ExecStepAction aep -> do
|
E.ExecStepAction (aep, _) -> do
|
||||||
(time, (r, _)) <- doQErr $ EA.runActionExecution aep
|
(time, (r, _)) <- doQErr $ EA.runActionExecution aep
|
||||||
pure $ ResultsFragment time Telem.Empty r []
|
pure $ ResultsFragment time Telem.Empty r []
|
||||||
E.ExecStepRaw json ->
|
E.ExecStepRaw json ->
|
||||||
@ -298,7 +305,7 @@ runGQ env logger reqId userInfo ipAddress reqHeaders queryType reqUnparsed = do
|
|||||||
return $ ResultsFragment telemTimeIO_DT Telem.Local resp responseHeaders
|
return $ ResultsFragment telemTimeIO_DT Telem.Local resp responseHeaders
|
||||||
E.ExecStepRemote rsi gqlReq ->
|
E.ExecStepRemote rsi gqlReq ->
|
||||||
runRemoteGQ httpManager fieldName rsi gqlReq
|
runRemoteGQ httpManager fieldName rsi gqlReq
|
||||||
E.ExecStepAction aep -> do
|
E.ExecStepAction (aep, _) -> do
|
||||||
(time, (r, hdrs)) <- doQErr $ EA.runActionExecution aep
|
(time, (r, hdrs)) <- doQErr $ EA.runActionExecution aep
|
||||||
pure $ ResultsFragment time Telem.Empty r $ fromMaybe [] hdrs
|
pure $ ResultsFragment time Telem.Empty r $ fromMaybe [] hdrs
|
||||||
E.ExecStepRaw json ->
|
E.ExecStepRaw json ->
|
||||||
@ -314,6 +321,10 @@ runGQ env logger reqId userInfo ipAddress reqHeaders queryType reqUnparsed = do
|
|||||||
Telem.recordTimingMetric Telem.RequestDimensions{..} Telem.RequestTimings{..}
|
Telem.recordTimingMetric Telem.RequestDimensions{..} Telem.RequestTimings{..}
|
||||||
return (normalizedSelectionSet, resp)
|
return (normalizedSelectionSet, resp)
|
||||||
where
|
where
|
||||||
|
getExecStepActionWithActionInfo acc execStep = case execStep of
|
||||||
|
EB.ExecStepAction (_, actionInfo) -> (actionInfo:acc)
|
||||||
|
_ -> acc
|
||||||
|
|
||||||
doQErr = withExceptT Right
|
doQErr = withExceptT Right
|
||||||
|
|
||||||
forWithKey = flip OMap.traverseWithKey
|
forWithKey = flip OMap.traverseWithKey
|
||||||
|
@ -385,9 +385,14 @@ onStart env serverEnv wsConn (StartMsg opId q) = catchAndIgnore $ do
|
|||||||
E.ExecStepDB _remoteHeaders exists ->
|
E.ExecStepDB _remoteHeaders exists ->
|
||||||
AB.dispatchAnyBackend @BackendTransport exists EB.getRemoteSchemaInfo
|
AB.dispatchAnyBackend @BackendTransport exists EB.getRemoteSchemaInfo
|
||||||
_ -> []
|
_ -> []
|
||||||
|
actionsInfo = foldl getExecStepActionWithActionInfo [] $ OMap.elems $ OMap.filter (\x -> case x of
|
||||||
|
E.ExecStepAction (_, _) -> True
|
||||||
|
_ -> False
|
||||||
|
) queryPlan
|
||||||
|
|
||||||
-- We ignore the response headers (containing TTL information) because
|
-- We ignore the response headers (containing TTL information) because
|
||||||
-- WebSockets don't support them.
|
-- WebSockets don't support them.
|
||||||
(_responseHeaders, cachedValue) <- Tracing.interpTraceT (withExceptT mempty) $ cacheLookup remoteJoins cacheKey
|
(_responseHeaders, cachedValue) <- Tracing.interpTraceT (withExceptT mempty) $ cacheLookup remoteJoins actionsInfo cacheKey
|
||||||
case cachedValue of
|
case cachedValue of
|
||||||
Just cachedResponseData -> do
|
Just cachedResponseData -> do
|
||||||
sendSuccResp cachedResponseData $ LQ.LiveQueryMetadata 0
|
sendSuccResp cachedResponseData $ LQ.LiveQueryMetadata 0
|
||||||
@ -409,7 +414,7 @@ onStart env serverEnv wsConn (StartMsg opId q) = catchAndIgnore $ do
|
|||||||
return $ ResultsFragment telemTimeIO_DT Telem.Local resp []
|
return $ ResultsFragment telemTimeIO_DT Telem.Local resp []
|
||||||
E.ExecStepRemote rsi gqlReq -> do
|
E.ExecStepRemote rsi gqlReq -> do
|
||||||
runRemoteGQ fieldName userInfo reqHdrs rsi gqlReq
|
runRemoteGQ fieldName userInfo reqHdrs rsi gqlReq
|
||||||
E.ExecStepAction actionExecPlan -> do
|
E.ExecStepAction (actionExecPlan, _) -> do
|
||||||
(time, (r, _)) <- doQErr $ EA.runActionExecution actionExecPlan
|
(time, (r, _)) <- doQErr $ EA.runActionExecution actionExecPlan
|
||||||
pure $ ResultsFragment time Telem.Empty r []
|
pure $ ResultsFragment time Telem.Empty r []
|
||||||
E.ExecStepRaw json ->
|
E.ExecStepRaw json ->
|
||||||
@ -458,7 +463,7 @@ onStart env serverEnv wsConn (StartMsg opId q) = catchAndIgnore $ do
|
|||||||
tx
|
tx
|
||||||
genSql
|
genSql
|
||||||
return $ ResultsFragment telemTimeIO_DT Telem.Local resp []
|
return $ ResultsFragment telemTimeIO_DT Telem.Local resp []
|
||||||
E.ExecStepAction actionExecPlan -> do
|
E.ExecStepAction (actionExecPlan, _) -> do
|
||||||
(time, (r, hdrs)) <- doQErr $ EA.runActionExecution actionExecPlan
|
(time, (r, hdrs)) <- doQErr $ EA.runActionExecution actionExecPlan
|
||||||
pure $ ResultsFragment time Telem.Empty r $ fromMaybe [] hdrs
|
pure $ ResultsFragment time Telem.Empty r $ fromMaybe [] hdrs
|
||||||
E.ExecStepRemote rsi gqlReq -> do
|
E.ExecStepRemote rsi gqlReq -> do
|
||||||
@ -522,6 +527,10 @@ onStart env serverEnv wsConn (StartMsg opId q) = catchAndIgnore $ do
|
|||||||
|
|
||||||
liftIO $ logOpEv ODStarted (Just requestId)
|
liftIO $ logOpEv ODStarted (Just requestId)
|
||||||
where
|
where
|
||||||
|
getExecStepActionWithActionInfo acc execStep = case execStep of
|
||||||
|
E.ExecStepAction (_, actionInfo) -> (actionInfo:acc)
|
||||||
|
_ -> acc
|
||||||
|
|
||||||
doQErr = withExceptT Right
|
doQErr = withExceptT Right
|
||||||
|
|
||||||
forWithKey = flip OMap.traverseWithKey
|
forWithKey = flip OMap.traverseWithKey
|
||||||
@ -652,7 +661,6 @@ onStart env serverEnv wsConn (StartMsg opId q) = catchAndIgnore $ do
|
|||||||
catchAndIgnore :: ExceptT () m () -> m ()
|
catchAndIgnore :: ExceptT () m () -> m ()
|
||||||
catchAndIgnore m = void $ runExceptT m
|
catchAndIgnore m = void $ runExceptT m
|
||||||
|
|
||||||
|
|
||||||
onMessage
|
onMessage
|
||||||
:: ( HasVersion
|
:: ( HasVersion
|
||||||
, MonadIO m
|
, MonadIO m
|
||||||
|
@ -705,8 +705,9 @@ buildSchemaCacheRule env = proc (metadata, invalidationKeys) -> do
|
|||||||
runExceptT $ resolveAction env resolvedCustomTypes def scalarsMap
|
runExceptT $ resolveAction env resolvedCustomTypes def scalarsMap
|
||||||
let permissionInfos = map (ActionPermissionInfo . _apmRole) actionPermissions
|
let permissionInfos = map (ActionPermissionInfo . _apmRole) actionPermissions
|
||||||
permissionMap = mapFromL _apiRole permissionInfos
|
permissionMap = mapFromL _apiRole permissionInfos
|
||||||
|
forwardClientHeaders = _adForwardClientHeaders resolvedDef
|
||||||
outputType = unGraphQLType $ _adOutputType def
|
outputType = unGraphQLType $ _adOutputType def
|
||||||
returnA -< ActionInfo name (outputType, outObject) resolvedDef permissionMap comment)
|
returnA -< ActionInfo name (outputType, outObject) resolvedDef permissionMap forwardClientHeaders comment)
|
||||||
|) addActionContext)
|
|) addActionContext)
|
||||||
|) (mkActionMetadataObject action)
|
|) (mkActionMetadataObject action)
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ module Hasura.RQL.Types.Action
|
|||||||
, aiOutputObject
|
, aiOutputObject
|
||||||
, aiDefinition
|
, aiDefinition
|
||||||
, aiPermissions
|
, aiPermissions
|
||||||
|
, aiForwardedClientHeaders
|
||||||
, aiComment
|
, aiComment
|
||||||
, defaultActionTimeoutSecs
|
, defaultActionTimeoutSecs
|
||||||
, ActionPermissionInfo(..)
|
, ActionPermissionInfo(..)
|
||||||
@ -61,6 +62,9 @@ module Hasura.RQL.Types.Action
|
|||||||
, ActionLogResponse(..)
|
, ActionLogResponse(..)
|
||||||
, ActionLogResponseMap
|
, ActionLogResponseMap
|
||||||
, AsyncActionStatus(..)
|
, AsyncActionStatus(..)
|
||||||
|
, ActionsInfo(..)
|
||||||
|
, asiName
|
||||||
|
, asiForwardClientHeaders
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
|
||||||
@ -208,6 +212,7 @@ data ActionInfo
|
|||||||
, _aiOutputObject :: !(G.GType, AnnotatedObjectType)
|
, _aiOutputObject :: !(G.GType, AnnotatedObjectType)
|
||||||
, _aiDefinition :: !ResolvedActionDefinition
|
, _aiDefinition :: !ResolvedActionDefinition
|
||||||
, _aiPermissions :: !ActionPermissionMap
|
, _aiPermissions :: !ActionPermissionMap
|
||||||
|
, _aiForwardedClientHeaders :: !Bool
|
||||||
, _aiComment :: !(Maybe Text)
|
, _aiComment :: !(Maybe Text)
|
||||||
} deriving (Generic)
|
} deriving (Generic)
|
||||||
instance J.ToJSON ActionInfo where
|
instance J.ToJSON ActionInfo where
|
||||||
@ -317,6 +322,7 @@ traverseAnnActionExecution f (AnnActionExecution n ot fs p oF dl w h fch sn to s
|
|||||||
data AnnActionMutationAsync
|
data AnnActionMutationAsync
|
||||||
= AnnActionMutationAsync
|
= AnnActionMutationAsync
|
||||||
{ _aamaName :: !ActionName
|
{ _aamaName :: !ActionName
|
||||||
|
, _aamaForwardClientHeaders :: !Bool
|
||||||
, _aamaPayload :: !J.Value -- ^ jsonified input arguments
|
, _aamaPayload :: !J.Value -- ^ jsonified input arguments
|
||||||
} deriving (Show, Eq)
|
} deriving (Show, Eq)
|
||||||
|
|
||||||
@ -335,9 +341,9 @@ traverseAsyncActionQueryField
|
|||||||
traverseAsyncActionQueryField f = \case
|
traverseAsyncActionQueryField f = \case
|
||||||
AsyncTypename t -> pure $ AsyncTypename t
|
AsyncTypename t -> pure $ AsyncTypename t
|
||||||
AsyncOutput fields -> AsyncOutput <$> traverse (traverse $ traverseAnnField f) fields
|
AsyncOutput fields -> AsyncOutput <$> traverse (traverse $ traverseAnnField f) fields
|
||||||
AsyncId -> pure $ AsyncId
|
AsyncId -> pure AsyncId
|
||||||
AsyncCreatedAt -> pure $ AsyncCreatedAt
|
AsyncCreatedAt -> pure AsyncCreatedAt
|
||||||
AsyncErrors -> pure $ AsyncErrors
|
AsyncErrors -> pure AsyncErrors
|
||||||
|
|
||||||
type AsyncActionQueryFieldsG b v = Fields (AsyncActionQueryFieldG b v)
|
type AsyncActionQueryFieldsG b v = Fields (AsyncActionQueryFieldG b v)
|
||||||
|
|
||||||
@ -349,6 +355,7 @@ data AnnActionAsyncQuery (b :: BackendType) v
|
|||||||
, _aaaqFields :: !(AsyncActionQueryFieldsG b v)
|
, _aaaqFields :: !(AsyncActionQueryFieldsG b v)
|
||||||
, _aaaqDefinitionList :: ![(Column b, ScalarType b)]
|
, _aaaqDefinitionList :: ![(Column b, ScalarType b)]
|
||||||
, _aaaqStringifyNum :: !Bool
|
, _aaaqStringifyNum :: !Bool
|
||||||
|
, _aaaqForwardClientHeaders :: !Bool
|
||||||
, _aaaqSource :: !(ActionSourceInfo b)
|
, _aaaqSource :: !(ActionSourceInfo b)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,8 +364,8 @@ traverseAnnActionAsyncQuery
|
|||||||
=> (a -> f b)
|
=> (a -> f b)
|
||||||
-> AnnActionAsyncQuery backend a
|
-> AnnActionAsyncQuery backend a
|
||||||
-> f (AnnActionAsyncQuery backend b)
|
-> f (AnnActionAsyncQuery backend b)
|
||||||
traverseAnnActionAsyncQuery f (AnnActionAsyncQuery n aid ot fs dl sn s) =
|
traverseAnnActionAsyncQuery f (AnnActionAsyncQuery n aid ot fs dl sn fch s) =
|
||||||
traverse (traverse $ traverseAsyncActionQueryField f) fs <&> \tfs -> AnnActionAsyncQuery n aid ot tfs dl sn s
|
traverse (traverse $ traverseAsyncActionQueryField f) fs <&> \tfs -> AnnActionAsyncQuery n aid ot tfs dl sn fch s
|
||||||
|
|
||||||
data ActionExecContext
|
data ActionExecContext
|
||||||
= ActionExecContext
|
= ActionExecContext
|
||||||
@ -397,3 +404,11 @@ type ActionLogResponseMap = HashMap ActionId ActionLogResponse
|
|||||||
data AsyncActionStatus
|
data AsyncActionStatus
|
||||||
= AASCompleted !J.Value
|
= AASCompleted !J.Value
|
||||||
| AASError !QErr
|
| AASError !QErr
|
||||||
|
|
||||||
|
data ActionsInfo
|
||||||
|
= ActionsInfo
|
||||||
|
{ _asiName :: !ActionName
|
||||||
|
, _asiForwardClientHeaders :: !Bool
|
||||||
|
}
|
||||||
|
deriving (Show, Eq, Generic)
|
||||||
|
$(makeLenses ''ActionsInfo)
|
||||||
|
@ -37,3 +37,45 @@ args:
|
|||||||
('Foo', 'Bar'),
|
('Foo', 'Bar'),
|
||||||
('Baz', 'Qux'),
|
('Baz', 'Qux'),
|
||||||
('X%20Y', 'Test');
|
('X%20Y', 'Test');
|
||||||
|
|
||||||
|
- type: run_sql
|
||||||
|
args:
|
||||||
|
sql: |
|
||||||
|
CREATE TABLE "user"(
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
email TEXT NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO "user" (name, email) VALUES ('Clarke 1', 'clarke1@gmail.com');
|
||||||
|
INSERT INTO "user" (name, email) VALUES ('Clarke 2', 'clarke2@gmail.com');
|
||||||
|
|
||||||
|
- type: set_custom_types
|
||||||
|
args:
|
||||||
|
objects:
|
||||||
|
- name: EmailResponse
|
||||||
|
fields:
|
||||||
|
- name: user
|
||||||
|
type: String!
|
||||||
|
|
||||||
|
- type: create_action
|
||||||
|
args:
|
||||||
|
name: get_user_by_email_1 # this action is being created with forward_client_headers set to `true`
|
||||||
|
definition:
|
||||||
|
type: query
|
||||||
|
arguments:
|
||||||
|
- name: email
|
||||||
|
type: String!
|
||||||
|
output_type: EmailResponse
|
||||||
|
handler: http://127.0.0.1:5593/get-user-by-email
|
||||||
|
forward_client_headers: true
|
||||||
|
|
||||||
|
- type: create_action
|
||||||
|
args:
|
||||||
|
name: get_user_by_email_2 # this action is being created with forward_client_headers set to `false`
|
||||||
|
definition:
|
||||||
|
type: query
|
||||||
|
arguments:
|
||||||
|
- name: email
|
||||||
|
type: String!
|
||||||
|
output_type: EmailResponse
|
||||||
|
handler: http://127.0.0.1:5593/get-user-by-email
|
||||||
|
@ -4,7 +4,16 @@ args:
|
|||||||
args:
|
args:
|
||||||
collection: test_collection
|
collection: test_collection
|
||||||
cascade: false
|
cascade: false
|
||||||
|
- type: drop_action
|
||||||
|
args:
|
||||||
|
name: get_user_by_email_1
|
||||||
|
- type: drop_action
|
||||||
|
args:
|
||||||
|
name: get_user_by_email_2
|
||||||
|
- type: set_custom_types
|
||||||
|
args: {}
|
||||||
- type: run_sql
|
- type: run_sql
|
||||||
args:
|
args:
|
||||||
sql: |
|
sql: |
|
||||||
drop table test_table
|
drop table test_table;
|
||||||
|
drop table user;
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
- description: Run get_user_by_email_1 query action with valid email, the response should be an error saying that caching is disabled
|
||||||
|
url: /v1/graphql
|
||||||
|
status: 400
|
||||||
|
query:
|
||||||
|
query: |
|
||||||
|
query ($email: String!) @cached(ttl: 300) {
|
||||||
|
get_user_by_email_1(email: $email){
|
||||||
|
user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
variables:
|
||||||
|
email: clarke1@gmail.com
|
||||||
|
|
||||||
|
response:
|
||||||
|
errors:
|
||||||
|
- extensions:
|
||||||
|
path: $
|
||||||
|
code: not-supported
|
||||||
|
message: 'Actions which forward client headers cannot currently be cached'
|
@ -0,0 +1,20 @@
|
|||||||
|
- description: Run get_user_by_email_2 query action with valid email, the response should be an object and response should be cached
|
||||||
|
url: /v1/graphql
|
||||||
|
status: 200
|
||||||
|
query:
|
||||||
|
query: |
|
||||||
|
query ($email: String!) @cached(ttl: 300) {
|
||||||
|
get_user_by_email_2(email: $email){
|
||||||
|
user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
variables:
|
||||||
|
email: clarke2@gmail.com
|
||||||
|
resp_headers:
|
||||||
|
Cache-Control: max-age=300
|
||||||
|
X-Hasura-Query-Cache-Key: 928537e2f3e76a263eaaa45686e0c0bccd2a9b7e
|
||||||
|
X-Hasura-Query-Family-Cache-Key: ae504392da7b9e98ee4679693b8bc0efd350e0d7
|
||||||
|
response:
|
||||||
|
data:
|
||||||
|
get_user_by_email_2:
|
||||||
|
user: Clarke 2
|
@ -50,3 +50,11 @@ class TestQueryCache:
|
|||||||
def test_no_variables_in_query_key(self, hge_ctx, transport):
|
def test_no_variables_in_query_key(self, hge_ctx, transport):
|
||||||
check_query_f(hge_ctx, self.dir() + '/test_no_variables_in_query_key.yaml', transport)
|
check_query_f(hge_ctx, self.dir() + '/test_no_variables_in_query_key.yaml', transport)
|
||||||
self.flushRedis()
|
self.flushRedis()
|
||||||
|
|
||||||
|
def test_action_query_with_forward_client_headers_set(self, hge_ctx, transport):
|
||||||
|
check_query_f(hge_ctx, self.dir() + '/test_action_query_with_forward_client_headers_set.yaml', transport)
|
||||||
|
self.flushRedis()
|
||||||
|
|
||||||
|
def test_action_query_with_forward_client_header_unset(self, hge_ctx, transport):
|
||||||
|
check_query_f(hge_ctx, self.dir() + '/test_action_query_with_forward_client_headers_unset.yaml', transport)
|
||||||
|
self.flushRedis()
|
||||||
|
Loading…
Reference in New Issue
Block a user