mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 08:02:15 +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 MSSQL table name descriptions
|
||||
- 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: display collection names and queries from all collections in allowlist
|
||||
- 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:
|
||||
|
||||
- The query does not make use of remote schemas or remote joins
|
||||
- The response JSON is under 100KB in size
|
||||
- The query does **not** make use of ``remote schemas`` or ``remote joins``.
|
||||
- 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
|
||||
|
||||
|
@ -721,7 +721,7 @@ instance HttpLog PGMetadataStorageApp where
|
||||
mkHttpAccessLogContext userInfoM reqId waiReq compressedResponse qTime cType headers
|
||||
|
||||
instance MonadExecuteQuery PGMetadataStorageApp where
|
||||
cacheLookup _ _ = pure ([], Nothing)
|
||||
cacheLookup _ _ _ = pure ([], Nothing)
|
||||
cacheStore _ _ = pure ()
|
||||
|
||||
instance UserAuthentication (Tracing.TraceT PGMetadataStorageApp) where
|
||||
|
@ -185,7 +185,7 @@ resolveActionMutationAsync
|
||||
resolveActionMutationAsync annAction reqHeaders sessionVariables =
|
||||
insertAction actionName sessionVariables reqHeaders inputArgs
|
||||
where
|
||||
AnnActionMutationAsync actionName inputArgs = annAction
|
||||
AnnActionMutationAsync actionName _ inputArgs = annAction
|
||||
|
||||
{- Note: [Resolving async action query]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -260,7 +260,7 @@ resolveAsyncActionQuery userInfo annAction =
|
||||
|
||||
in RS.AnnSelectG annotatedFields tableFromExp tablePermissions tableArguments stringifyNumerics
|
||||
where
|
||||
AnnActionAsyncQuery _ actionId outputType asyncFields definitionList stringifyNumerics actionSource = annAction
|
||||
AnnActionAsyncQuery _ actionId outputType asyncFields definitionList stringifyNumerics _ actionSource = annAction
|
||||
|
||||
idColumn = (unsafePGCol "id", PGUUID)
|
||||
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.Parser hiding (Type)
|
||||
import Hasura.RQL.IR.RemoteJoin
|
||||
import Hasura.RQL.Types.Action
|
||||
import Hasura.RQL.Types.Backend
|
||||
import Hasura.RQL.Types.Common
|
||||
import Hasura.RQL.Types.Error
|
||||
@ -100,7 +101,7 @@ data ExecutionStep where
|
||||
-> ExecutionStep
|
||||
-- ^ A query to execute against the database
|
||||
ExecStepAction
|
||||
:: ActionExecutionPlan
|
||||
:: (ActionExecutionPlan, ActionsInfo)
|
||||
-> ExecutionStep
|
||||
-- ^ Execute an action
|
||||
ExecStepRemote
|
||||
|
@ -89,11 +89,15 @@ convertMutationSelectionSet env logger gqlContext SQLGenCtx{stringifyNum} userIn
|
||||
RFRemote remoteField -> do
|
||||
RemoteFieldG remoteSchemaInfo resolvedRemoteField <- resolveRemoteField userInfo remoteField
|
||||
pure $ buildExecStepRemote remoteSchemaInfo G.OperationTypeMutation $ [G.SelectionField resolvedRemoteField]
|
||||
RFAction action ->
|
||||
ExecStepAction <$> convertMutationAction env logger userInfo manager reqHeaders action
|
||||
RFAction action -> do
|
||||
(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 ->
|
||||
pure $ ExecStepRaw s
|
||||
|
||||
return (txs, resolvedSelSet)
|
||||
where
|
||||
reportParseErrors errs = case NE.head errs of
|
||||
|
@ -86,10 +86,11 @@ convertQuerySelSet env logger gqlContext userInfo manager reqHeaders directives
|
||||
RFRemote rf -> do
|
||||
RemoteFieldG remoteSchemaInfo remoteField <- for rf $ resolveRemoteVariable userInfo
|
||||
pure $ buildExecStepRemote remoteSchemaInfo G.OperationTypeQuery [G.SelectionField remoteField]
|
||||
RFAction a ->
|
||||
pure $ ExecStepAction $ case a of
|
||||
AQQuery s -> AEPSync $ resolveActionExecution env logger userInfo s (ActionExecContext manager reqHeaders usrVars)
|
||||
AQAsync s -> AEPAsyncQuery $ AsyncActionQueryExecutionPlan (_aaaqActionId s) $ resolveAsyncActionQuery userInfo s
|
||||
RFAction a -> do
|
||||
(action, actionName, fch) <- pure $ case a of
|
||||
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, _aaaqName s, _aaaqForwardClientHeaders s)
|
||||
pure $ ExecStepAction (action, (ActionsInfo actionName fch))
|
||||
RFRaw r ->
|
||||
pure $ ExecStepRaw r
|
||||
|
||||
|
@ -55,7 +55,7 @@ actionExecute
|
||||
-> m (Maybe (FieldParser n (AnnActionExecution 'Postgres (UnpreparedValue 'Postgres))))
|
||||
actionExecute nonObjectTypeMap actionInfo = runMaybeT do
|
||||
roleName <- askRoleName
|
||||
guard $ (roleName == adminRoleName || roleName `Map.member` permissions)
|
||||
guard (roleName == adminRoleName || roleName `Map.member` permissions)
|
||||
let fieldName = unActionName actionName
|
||||
description = G.Description <$> comment
|
||||
inputArguments <- lift $ actionInputArguments nonObjectTypeMap $ _adArguments definition
|
||||
@ -77,7 +77,7 @@ actionExecute nonObjectTypeMap actionInfo = runMaybeT do
|
||||
, _aaeSource = getActionSourceInfo outputObject
|
||||
}
|
||||
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
|
||||
-- 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
|
||||
description = G.Description <$> comment
|
||||
pure $ P.selection fieldName description inputArguments actionIdParser
|
||||
<&> AnnActionMutationAsync actionName
|
||||
<&> AnnActionMutationAsync actionName forwardClientHeaders
|
||||
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
|
||||
-- asynchronous mutation action. The only input argument to an
|
||||
@ -165,10 +165,11 @@ actionAsyncQuery actionInfo = runMaybeT do
|
||||
, _aaaqFields = fields
|
||||
, _aaaqDefinitionList = mkDefinitionList outputObject
|
||||
, _aaaqStringifyNum = stringifyNum
|
||||
, _aaaqForwardClientHeaders = forwardClientHeaders
|
||||
, _aaaqSource = getActionSourceInfo outputObject
|
||||
}
|
||||
where
|
||||
ActionInfo actionName (outputType, outputObject) definition permissions comment = actionInfo
|
||||
ActionInfo actionName (outputType, outputObject) definition permissions forwardClientHeaders comment = actionInfo
|
||||
idFieldName = $$(G.litName "id")
|
||||
idFieldDescription = "the unique id of an action"
|
||||
|
||||
|
@ -86,6 +86,8 @@ class Monad m => MonadExecuteQuery m where
|
||||
cacheLookup
|
||||
:: [RemoteSchemaInfo]
|
||||
-- ^ 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
|
||||
-- ^ Key that uniquely identifies the result of a query execution
|
||||
-> TraceT (ExceptT QErr m) (HTTP.ResponseHeaders, Maybe EncJSON)
|
||||
@ -112,19 +114,19 @@ class Monad m => MonadExecuteQuery m where
|
||||
-- ^ Always succeeds
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
-- | 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 ->
|
||||
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
|
||||
Just cachedResponseData ->
|
||||
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 []
|
||||
E.ExecStepRemote rsi gqlReq ->
|
||||
runRemoteGQ httpManager fieldName rsi gqlReq
|
||||
E.ExecStepAction aep -> do
|
||||
E.ExecStepAction (aep, _) -> do
|
||||
(time, (r, _)) <- doQErr $ EA.runActionExecution aep
|
||||
pure $ ResultsFragment time Telem.Empty r []
|
||||
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
|
||||
E.ExecStepRemote rsi gqlReq ->
|
||||
runRemoteGQ httpManager fieldName rsi gqlReq
|
||||
E.ExecStepAction aep -> do
|
||||
E.ExecStepAction (aep, _) -> do
|
||||
(time, (r, hdrs)) <- doQErr $ EA.runActionExecution aep
|
||||
pure $ ResultsFragment time Telem.Empty r $ fromMaybe [] hdrs
|
||||
E.ExecStepRaw json ->
|
||||
@ -314,6 +321,10 @@ runGQ env logger reqId userInfo ipAddress reqHeaders queryType reqUnparsed = do
|
||||
Telem.recordTimingMetric Telem.RequestDimensions{..} Telem.RequestTimings{..}
|
||||
return (normalizedSelectionSet, resp)
|
||||
where
|
||||
getExecStepActionWithActionInfo acc execStep = case execStep of
|
||||
EB.ExecStepAction (_, actionInfo) -> (actionInfo:acc)
|
||||
_ -> acc
|
||||
|
||||
doQErr = withExceptT Right
|
||||
|
||||
forWithKey = flip OMap.traverseWithKey
|
||||
|
@ -385,9 +385,14 @@ onStart env serverEnv wsConn (StartMsg opId q) = catchAndIgnore $ do
|
||||
E.ExecStepDB _remoteHeaders exists ->
|
||||
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
|
||||
-- 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
|
||||
Just cachedResponseData -> do
|
||||
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 []
|
||||
E.ExecStepRemote rsi gqlReq -> do
|
||||
runRemoteGQ fieldName userInfo reqHdrs rsi gqlReq
|
||||
E.ExecStepAction actionExecPlan -> do
|
||||
E.ExecStepAction (actionExecPlan, _) -> do
|
||||
(time, (r, _)) <- doQErr $ EA.runActionExecution actionExecPlan
|
||||
pure $ ResultsFragment time Telem.Empty r []
|
||||
E.ExecStepRaw json ->
|
||||
@ -458,7 +463,7 @@ onStart env serverEnv wsConn (StartMsg opId q) = catchAndIgnore $ do
|
||||
tx
|
||||
genSql
|
||||
return $ ResultsFragment telemTimeIO_DT Telem.Local resp []
|
||||
E.ExecStepAction actionExecPlan -> do
|
||||
E.ExecStepAction (actionExecPlan, _) -> do
|
||||
(time, (r, hdrs)) <- doQErr $ EA.runActionExecution actionExecPlan
|
||||
pure $ ResultsFragment time Telem.Empty r $ fromMaybe [] hdrs
|
||||
E.ExecStepRemote rsi gqlReq -> do
|
||||
@ -522,6 +527,10 @@ onStart env serverEnv wsConn (StartMsg opId q) = catchAndIgnore $ do
|
||||
|
||||
liftIO $ logOpEv ODStarted (Just requestId)
|
||||
where
|
||||
getExecStepActionWithActionInfo acc execStep = case execStep of
|
||||
E.ExecStepAction (_, actionInfo) -> (actionInfo:acc)
|
||||
_ -> acc
|
||||
|
||||
doQErr = withExceptT Right
|
||||
|
||||
forWithKey = flip OMap.traverseWithKey
|
||||
@ -652,7 +661,6 @@ onStart env serverEnv wsConn (StartMsg opId q) = catchAndIgnore $ do
|
||||
catchAndIgnore :: ExceptT () m () -> m ()
|
||||
catchAndIgnore m = void $ runExceptT m
|
||||
|
||||
|
||||
onMessage
|
||||
:: ( HasVersion
|
||||
, MonadIO m
|
||||
|
@ -705,8 +705,9 @@ buildSchemaCacheRule env = proc (metadata, invalidationKeys) -> do
|
||||
runExceptT $ resolveAction env resolvedCustomTypes def scalarsMap
|
||||
let permissionInfos = map (ActionPermissionInfo . _apmRole) actionPermissions
|
||||
permissionMap = mapFromL _apiRole permissionInfos
|
||||
forwardClientHeaders = _adForwardClientHeaders resolvedDef
|
||||
outputType = unGraphQLType $ _adOutputType def
|
||||
returnA -< ActionInfo name (outputType, outObject) resolvedDef permissionMap comment)
|
||||
returnA -< ActionInfo name (outputType, outObject) resolvedDef permissionMap forwardClientHeaders comment)
|
||||
|) addActionContext)
|
||||
|) (mkActionMetadataObject action)
|
||||
|
||||
|
@ -31,6 +31,7 @@ module Hasura.RQL.Types.Action
|
||||
, aiOutputObject
|
||||
, aiDefinition
|
||||
, aiPermissions
|
||||
, aiForwardedClientHeaders
|
||||
, aiComment
|
||||
, defaultActionTimeoutSecs
|
||||
, ActionPermissionInfo(..)
|
||||
@ -61,6 +62,9 @@ module Hasura.RQL.Types.Action
|
||||
, ActionLogResponse(..)
|
||||
, ActionLogResponseMap
|
||||
, AsyncActionStatus(..)
|
||||
, ActionsInfo(..)
|
||||
, asiName
|
||||
, asiForwardClientHeaders
|
||||
) where
|
||||
|
||||
|
||||
@ -204,11 +208,12 @@ getActionOutputFields =
|
||||
|
||||
data ActionInfo
|
||||
= ActionInfo
|
||||
{ _aiName :: !ActionName
|
||||
, _aiOutputObject :: !(G.GType, AnnotatedObjectType)
|
||||
, _aiDefinition :: !ResolvedActionDefinition
|
||||
, _aiPermissions :: !ActionPermissionMap
|
||||
, _aiComment :: !(Maybe Text)
|
||||
{ _aiName :: !ActionName
|
||||
, _aiOutputObject :: !(G.GType, AnnotatedObjectType)
|
||||
, _aiDefinition :: !ResolvedActionDefinition
|
||||
, _aiPermissions :: !ActionPermissionMap
|
||||
, _aiForwardedClientHeaders :: !Bool
|
||||
, _aiComment :: !(Maybe Text)
|
||||
} deriving (Generic)
|
||||
instance J.ToJSON ActionInfo where
|
||||
toJSON = J.genericToJSON hasuraJSON
|
||||
@ -316,8 +321,9 @@ traverseAnnActionExecution f (AnnActionExecution n ot fs p oF dl w h fch sn to s
|
||||
|
||||
data AnnActionMutationAsync
|
||||
= AnnActionMutationAsync
|
||||
{ _aamaName :: !ActionName
|
||||
, _aamaPayload :: !J.Value -- ^ jsonified input arguments
|
||||
{ _aamaName :: !ActionName
|
||||
, _aamaForwardClientHeaders :: !Bool
|
||||
, _aamaPayload :: !J.Value -- ^ jsonified input arguments
|
||||
} deriving (Show, Eq)
|
||||
|
||||
data AsyncActionQueryFieldG (b :: BackendType) v
|
||||
@ -335,21 +341,22 @@ traverseAsyncActionQueryField
|
||||
traverseAsyncActionQueryField f = \case
|
||||
AsyncTypename t -> pure $ AsyncTypename t
|
||||
AsyncOutput fields -> AsyncOutput <$> traverse (traverse $ traverseAnnField f) fields
|
||||
AsyncId -> pure $ AsyncId
|
||||
AsyncCreatedAt -> pure $ AsyncCreatedAt
|
||||
AsyncErrors -> pure $ AsyncErrors
|
||||
AsyncId -> pure AsyncId
|
||||
AsyncCreatedAt -> pure AsyncCreatedAt
|
||||
AsyncErrors -> pure AsyncErrors
|
||||
|
||||
type AsyncActionQueryFieldsG b v = Fields (AsyncActionQueryFieldG b v)
|
||||
|
||||
data AnnActionAsyncQuery (b :: BackendType) v
|
||||
= AnnActionAsyncQuery
|
||||
{ _aaaqName :: !ActionName
|
||||
, _aaaqActionId :: !ActionId
|
||||
, _aaaqOutputType :: !GraphQLType
|
||||
, _aaaqFields :: !(AsyncActionQueryFieldsG b v)
|
||||
, _aaaqDefinitionList :: ![(Column b, ScalarType b)]
|
||||
, _aaaqStringifyNum :: !Bool
|
||||
, _aaaqSource :: !(ActionSourceInfo b)
|
||||
{ _aaaqName :: !ActionName
|
||||
, _aaaqActionId :: !ActionId
|
||||
, _aaaqOutputType :: !GraphQLType
|
||||
, _aaaqFields :: !(AsyncActionQueryFieldsG b v)
|
||||
, _aaaqDefinitionList :: ![(Column b, ScalarType b)]
|
||||
, _aaaqStringifyNum :: !Bool
|
||||
, _aaaqForwardClientHeaders :: !Bool
|
||||
, _aaaqSource :: !(ActionSourceInfo b)
|
||||
}
|
||||
|
||||
traverseAnnActionAsyncQuery
|
||||
@ -357,8 +364,8 @@ traverseAnnActionAsyncQuery
|
||||
=> (a -> f b)
|
||||
-> AnnActionAsyncQuery backend a
|
||||
-> f (AnnActionAsyncQuery backend b)
|
||||
traverseAnnActionAsyncQuery f (AnnActionAsyncQuery n aid ot fs dl sn s) =
|
||||
traverse (traverse $ traverseAsyncActionQueryField f) fs <&> \tfs -> AnnActionAsyncQuery n aid ot tfs 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 fch s
|
||||
|
||||
data ActionExecContext
|
||||
= ActionExecContext
|
||||
@ -397,3 +404,11 @@ type ActionLogResponseMap = HashMap ActionId ActionLogResponse
|
||||
data AsyncActionStatus
|
||||
= AASCompleted !J.Value
|
||||
| AASError !QErr
|
||||
|
||||
data ActionsInfo
|
||||
= ActionsInfo
|
||||
{ _asiName :: !ActionName
|
||||
, _asiForwardClientHeaders :: !Bool
|
||||
}
|
||||
deriving (Show, Eq, Generic)
|
||||
$(makeLenses ''ActionsInfo)
|
||||
|
@ -37,3 +37,45 @@ args:
|
||||
('Foo', 'Bar'),
|
||||
('Baz', 'Qux'),
|
||||
('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:
|
||||
collection: test_collection
|
||||
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
|
||||
args:
|
||||
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):
|
||||
check_query_f(hge_ctx, self.dir() + '/test_no_variables_in_query_key.yaml', transport)
|
||||
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