From c519187de72d7719aa3b88258a5eb0473f864a8b Mon Sep 17 00:00:00 2001 From: Russell Owen Date: Thu, 15 Oct 2020 08:46:34 -0700 Subject: [PATCH 1/3] Fix enable_async=True in aiohttp Apply the fix suggested by ketanbshah in https://github.com/graphql-python/graphql-server/issues/64 --- graphql_server/aiohttp/graphqlview.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/graphql_server/aiohttp/graphqlview.py b/graphql_server/aiohttp/graphqlview.py index 84a5f11..61d2a3d 100644 --- a/graphql_server/aiohttp/graphqlview.py +++ b/graphql_server/aiohttp/graphqlview.py @@ -4,7 +4,7 @@ from typing import List from aiohttp import web -from graphql import GraphQLError +from graphql import ExecutionResult, GraphQLError from graphql.type.schema import GraphQLSchema from graphql_server import ( @@ -152,7 +152,10 @@ async def __call__(self, request): ) exec_res = ( - [await ex for ex in execution_results] + [ + ex if ex is None or isinstance(ex, ExecutionResult) else await ex + for ex in execution_results + ] if self.enable_async else execution_results ) From 3ca0a98802599ef70e6f86238768d8a070e4e55a Mon Sep 17 00:00:00 2001 From: Russell Owen Date: Thu, 15 Oct 2020 08:47:42 -0700 Subject: [PATCH 2/3] Apply the same fix to sanic --- graphql_server/sanic/graphqlview.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/graphql_server/sanic/graphqlview.py b/graphql_server/sanic/graphqlview.py index 110ea2e..29548e9 100644 --- a/graphql_server/sanic/graphqlview.py +++ b/graphql_server/sanic/graphqlview.py @@ -4,7 +4,7 @@ from functools import partial from typing import List -from graphql import GraphQLError +from graphql import ExecutionResult, GraphQLError from graphql.type.schema import GraphQLSchema from sanic.response import HTTPResponse, html from sanic.views import HTTPMethodView @@ -105,7 +105,12 @@ async def dispatch_request(self, request, *args, **kwargs): middleware=self.get_middleware(), ) exec_res = ( - [await ex for ex in execution_results] + [ + ex + if ex is None or isinstance(ex, ExecutionResult) + else await ex + for ex in execution_results + ] if self.enable_async else execution_results ) From 379fd908981c595f9962aace1d327920d24ffac7 Mon Sep 17 00:00:00 2001 From: KingDarBoja Date: Sat, 17 Oct 2020 15:20:37 -0500 Subject: [PATCH 3/3] tests: add tests for graphiql enabled plus async --- tests/aiohttp/schema.py | 18 +++++++++++ tests/aiohttp/test_graphiqlview.py | 48 +++++++++++++++++++++++++++--- tests/sanic/schema.py | 18 +++++++++++ tests/sanic/test_graphiqlview.py | 31 +++++++++++++++++-- 4 files changed, 108 insertions(+), 7 deletions(-) diff --git a/tests/aiohttp/schema.py b/tests/aiohttp/schema.py index 6e5495a..7673180 100644 --- a/tests/aiohttp/schema.py +++ b/tests/aiohttp/schema.py @@ -91,4 +91,22 @@ def resolver_field_sync(_obj, info): ) +def resolver_field_sync_1(_obj, info): + return "synced_one" + + +def resolver_field_sync_2(_obj, info): + return "synced_two" + + +SyncQueryType = GraphQLObjectType( + "SyncQueryType", + { + "a": GraphQLField(GraphQLString, resolve=resolver_field_sync_1), + "b": GraphQLField(GraphQLString, resolve=resolver_field_sync_2), + }, +) + + AsyncSchema = GraphQLSchema(AsyncQueryType) +SyncSchema = GraphQLSchema(SyncQueryType) diff --git a/tests/aiohttp/test_graphiqlview.py b/tests/aiohttp/test_graphiqlview.py index a4a7a26..111b603 100644 --- a/tests/aiohttp/test_graphiqlview.py +++ b/tests/aiohttp/test_graphiqlview.py @@ -3,7 +3,7 @@ from jinja2 import Environment from tests.aiohttp.app import create_app, url_string -from tests.aiohttp.schema import AsyncSchema, Schema +from tests.aiohttp.schema import AsyncSchema, Schema, SyncSchema @pytest.fixture @@ -102,11 +102,51 @@ async def test_graphiql_get_subscriptions(app, client): @pytest.mark.asyncio -@pytest.mark.parametrize("app", [create_app(schema=AsyncSchema, enable_async=True)]) -async def test_graphiql_async_schema(app, client): +@pytest.mark.parametrize( + "app", [create_app(schema=AsyncSchema, enable_async=True, graphiql=True)] +) +async def test_graphiql_enabled_async_schema(app, client): response = await client.get( url_string(query="{a,b,c}"), headers={"Accept": "text/html"}, ) + expected_response = ( + ( + "{\n" + ' "data": {\n' + ' "a": "hey",\n' + ' "b": "hey2",\n' + ' "c": "hey3"\n' + " }\n" + "}" + ) + .replace('"', '\\"') + .replace("\n", "\\n") + ) + assert response.status == 200 + assert expected_response in await response.text() + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "app", [create_app(schema=SyncSchema, enable_async=True, graphiql=True)] +) +async def test_graphiql_enabled_sync_schema(app, client): + response = await client.get( + url_string(query="{a,b}"), headers={"Accept": "text/html"}, + ) + + expected_response = ( + ( + "{\n" + ' "data": {\n' + ' "a": "synced_one",\n' + ' "b": "synced_two"\n' + " }\n" + "}" + ) + .replace('"', '\\"') + .replace("\n", "\\n") + ) assert response.status == 200 - assert await response.json() == {"data": {"a": "hey", "b": "hey2", "c": "hey3"}} + assert expected_response in await response.text() diff --git a/tests/sanic/schema.py b/tests/sanic/schema.py index f827c2b..3c3298f 100644 --- a/tests/sanic/schema.py +++ b/tests/sanic/schema.py @@ -78,4 +78,22 @@ def resolver_field_sync(_obj, info): }, ) + +def resolver_field_sync_1(_obj, info): + return "synced_one" + + +def resolver_field_sync_2(_obj, info): + return "synced_two" + + +SyncQueryType = GraphQLObjectType( + "SyncQueryType", + { + "a": GraphQLField(GraphQLString, resolve=resolver_field_sync_1), + "b": GraphQLField(GraphQLString, resolve=resolver_field_sync_2), + }, +) + AsyncSchema = GraphQLSchema(AsyncQueryType) +SyncSchema = GraphQLSchema(SyncQueryType) diff --git a/tests/sanic/test_graphiqlview.py b/tests/sanic/test_graphiqlview.py index 60ecc75..91711f0 100644 --- a/tests/sanic/test_graphiqlview.py +++ b/tests/sanic/test_graphiqlview.py @@ -2,7 +2,7 @@ from jinja2 import Environment from .app import create_app, url_string -from .schema import AsyncSchema +from .schema import AsyncSchema, SyncSchema @pytest.fixture @@ -62,9 +62,9 @@ def test_graphiql_html_is_not_accepted(app): @pytest.mark.parametrize( - "app", [create_app(graphiql=True, schema=AsyncSchema, enable_async=True)] + "app", [create_app(schema=AsyncSchema, enable_async=True, graphiql=True)] ) -def test_graphiql_asyncio_schema(app): +def test_graphiql_enabled_async_schema(app): query = "{a,b,c}" _, response = app.client.get( uri=url_string(query=query), headers={"Accept": "text/html"} @@ -86,3 +86,28 @@ def test_graphiql_asyncio_schema(app): assert response.status == 200 assert expected_response in response.body.decode("utf-8") + + +@pytest.mark.parametrize( + "app", [create_app(schema=SyncSchema, enable_async=True, graphiql=True)] +) +def test_graphiql_enabled_sync_schema(app): + query = "{a,b}" + _, response = app.client.get( + uri=url_string(query=query), headers={"Accept": "text/html"} + ) + + expected_response = ( + ( + "{\n" + ' "data": {\n' + ' "a": "synced_one",\n' + ' "b": "synced_two"\n' + " }\n" + "}" + ) + .replace('"', '\\"') + .replace("\n", "\\n") + ) + assert response.status == 200 + assert expected_response in response.body.decode("utf-8")