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:
Sameer Kolhar 2021-04-13 12:30:43 +05:30 committed by hasura-bot
parent 17dc201fef
commit b274a2d240
17 changed files with 192 additions and 50 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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'

View File

@ -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

View File

@ -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()