From 2e823234f9d25fda013f2f2650194b6c2cffb31a Mon Sep 17 00:00:00 2001 From: Naveen Naidu Date: Wed, 16 Feb 2022 13:24:21 +0530 Subject: [PATCH] server/remote-schema: Include OperationName in the request sent to remote schemas PR-URL: https://github.com/hasura/graphql-engine-mono/pull/3581 Co-authored-by: Puru Gupta <32328846+purugupta99@users.noreply.github.com> GitOrigin-RevId: 2db21d0df158a5a3229b883ee426b2f38f864f80 --- CHANGELOG.md | 2 +- .../Hasura/GraphQL/Execute/Mutation.hs | 2 +- .../src-lib/Hasura/GraphQL/Execute/Query.hs | 2 +- .../src-lib/Hasura/GraphQL/Execute/Remote.hs | 8 ++++--- server/tests-py/graphql_server.py | 21 +++++++++++++++++++ .../basic_query_with_op_name.yaml | 9 ++++++++ server/tests-py/test_schema_stitching.py | 21 +++++++++++++++++++ 7 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 server/tests-py/queries/remote_schemas/basic_query_with_op_name.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a421824d57..425df53b046 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ The optimization can be enabled using the ### Bug fixes and improvements (Add entries below in the order of server, console, cli, docs, others) - +- server: add operation name in the request sent to remote schemas - server: add support for scalar response types for actions (issue #7805) - server: fix nullable action response (issue #4405) - console: add support for remote database relationships diff --git a/server/src-lib/Hasura/GraphQL/Execute/Mutation.hs b/server/src-lib/Hasura/GraphQL/Execute/Mutation.hs index 849cbc642aa..6a9dc1dfd7e 100644 --- a/server/src-lib/Hasura/GraphQL/Execute/Mutation.hs +++ b/server/src-lib/Hasura/GraphQL/Execute/Mutation.hs @@ -128,7 +128,7 @@ convertMutationSelectionSet pure $ ExecStepDB [] (AB.mkAnyBackend dbStepInfo) remoteJoins RFRemote remoteField -> do RemoteFieldG remoteSchemaInfo resultCustomizer resolvedRemoteField <- runVariableCache $ resolveRemoteField userInfo remoteField - pure $ buildExecStepRemote remoteSchemaInfo resultCustomizer G.OperationTypeMutation [G.SelectionField resolvedRemoteField] + pure $ buildExecStepRemote remoteSchemaInfo resultCustomizer G.OperationTypeMutation [G.SelectionField resolvedRemoteField] (GH._grOperationName gqlUnparsed) RFAction action -> do let (noRelsDBAST, remoteJoins) = RJ.getRemoteJoinsActionMutation action (actionName, _fch) <- pure $ case noRelsDBAST of diff --git a/server/src-lib/Hasura/GraphQL/Execute/Query.hs b/server/src-lib/Hasura/GraphQL/Execute/Query.hs index 04f2e58d96d..97db50026f1 100644 --- a/server/src-lib/Hasura/GraphQL/Execute/Query.hs +++ b/server/src-lib/Hasura/GraphQL/Execute/Query.hs @@ -114,7 +114,7 @@ convertQuerySelSet pure $ ExecStepDB [] (AB.mkAnyBackend dbStepInfo) remoteJoins RFRemote rf -> do RemoteFieldG remoteSchemaInfo resultCustomizer remoteField <- runVariableCache $ for rf $ resolveRemoteVariable userInfo - pure $ buildExecStepRemote remoteSchemaInfo resultCustomizer G.OperationTypeQuery [G.SelectionField remoteField] + pure $ buildExecStepRemote remoteSchemaInfo resultCustomizer G.OperationTypeQuery [G.SelectionField remoteField] (GH._grOperationName gqlUnparsed) RFAction action -> do let (noRelsDBAST, remoteJoins) = RJ.getRemoteJoinsActionQuery action (actionExecution, actionName, fch) <- pure $ case noRelsDBAST of diff --git a/server/src-lib/Hasura/GraphQL/Execute/Remote.hs b/server/src-lib/Hasura/GraphQL/Execute/Remote.hs index 1dacb0b8a8b..56d042574f3 100644 --- a/server/src-lib/Hasura/GraphQL/Execute/Remote.hs +++ b/server/src-lib/Hasura/GraphQL/Execute/Remote.hs @@ -70,16 +70,18 @@ buildExecStepRemote :: ResultCustomizer -> G.OperationType -> G.SelectionSet G.NoFragments Variable -> + Maybe OperationName -> ExecutionStep -buildExecStepRemote remoteSchemaInfo resultCustomizer tp selSet = +buildExecStepRemote remoteSchemaInfo resultCustomizer tp selSet operationName = let unresolvedSelSet = unresolveVariables selSet allVars = map mkVariableDefinitionAndValue $ Set.toList $ collectVariables selSet varValues = Map.fromList $ map snd allVars varValsM = bool (Just varValues) Nothing $ Map.null varValues varDefs = map fst allVars - _grQuery = G.TypedOperationDefinition tp Nothing varDefs [] unresolvedSelSet + _grQuery = G.TypedOperationDefinition tp (_unOperationName <$> operationName) varDefs [] unresolvedSelSet _grVariables = varValsM - in ExecStepRemote remoteSchemaInfo resultCustomizer GH.GQLReq {_grOperationName = Nothing, ..} + _grOperationName = operationName + in ExecStepRemote remoteSchemaInfo resultCustomizer GH.GQLReq {..} -- | Association between keys uniquely identifying some remote JSON variable and -- an 'Int' identifier that will be used to construct a valid variable name to diff --git a/server/tests-py/graphql_server.py b/server/tests-py/graphql_server.py index 3b91dfe2574..b76088c1574 100644 --- a/server/tests-py/graphql_server.py +++ b/server/tests-py/graphql_server.py @@ -48,6 +48,26 @@ class HelloGraphQL(RequestHandler): res = hello_schema.execute(request.json['query']) return mkJSONResp(res) +class HelloGraphQLEchoRequest(RequestHandler): + def get(self, request): + return Response(HTTPStatus.METHOD_NOT_ALLOWED) + + def post(self, request): + if not request.json: + return Response(HTTPStatus.BAD_REQUEST) + res = hello_schema.execute(request.json['query']) + respDict = res.to_dict() + + # Return the result as it is, when we send an introspection query + if respDict.get('data',{}).get('__schema',{}): + return mkJSONResp(res) + # Edit the result to contain, the 'request payload' as part of the response. + # We can then use this to assert the request payload with the expected response. + else: + respDict.get('data', {})['hello'] = request.json + return Response(HTTPStatus.OK, res.to_dict(), + {'Content-Type': 'application/json'}) + class HelloGraphQLExtensions(RequestHandler): def get(self, request): return Response(HTTPStatus.METHOD_NOT_ALLOWED) @@ -808,6 +828,7 @@ class MessagesGraphQL(RequestHandler): handlers = MkHandlers({ '/hello': HelloWorldHandler, '/hello-graphql': HelloGraphQL, + '/hello-echo-request-graphql': HelloGraphQLEchoRequest, '/hello-graphql-extensions': HelloGraphQLExtensions, '/user-graphql': UserGraphQL, '/country-graphql': CountryGraphQL, diff --git a/server/tests-py/queries/remote_schemas/basic_query_with_op_name.yaml b/server/tests-py/queries/remote_schemas/basic_query_with_op_name.yaml new file mode 100644 index 00000000000..05412b4fcb0 --- /dev/null +++ b/server/tests-py/queries/remote_schemas/basic_query_with_op_name.yaml @@ -0,0 +1,9 @@ +description: Simple GraphQL query with Operation Name +url: /v1/graphql +status: 200 +query: + query: | + query HelloMe { + hello + } + operationName: HelloMe diff --git a/server/tests-py/test_schema_stitching.py b/server/tests-py/test_schema_stitching.py index 168d323a9db..6c16351b8fa 100644 --- a/server/tests-py/test_schema_stitching.py +++ b/server/tests-py/test_schema_stitching.py @@ -884,3 +884,24 @@ class TestValidateRemoteSchemaCustomizeAllTheThings: def test_remote_schema_customize_all_the_things(self, hge_ctx): check_query_f(hge_ctx, self.dir() + '/customize_all_the_things.yaml') + +class TestRemoteSchemaRequestPayload: + dir = 'queries/remote_schemas' + teardown = {"type": "clear_metadata", "args": {}} + + @pytest.fixture(autouse=True) + def transact(self, hge_ctx): + q = mk_add_remote_q('echo request', 'http://localhost:5000/hello-echo-request-graphql') + st_code, resp = hge_ctx.v1q(q) + assert st_code == 200, resp + yield + hge_ctx.v1q(self.teardown) + + @pytest.mark.allow_server_upgrade_test + def test_remote_schema_operation_name_in_response(self, hge_ctx): + + with open('queries/remote_schemas/basic_query_with_op_name.yaml') as f: + query = yaml.load(f) + resp, _ = check_query(hge_ctx, query) + + assert resp['data']['hello']['operationName'] == "HelloMe" \ No newline at end of file