From 49f73c3aaa8d00054aef54524908d09828952b3b Mon Sep 17 00:00:00 2001 From: Manuel Bojato <30560560+KingDarBoja@users.noreply.github.com> Date: Sat, 17 Oct 2020 13:19:10 -0500 Subject: [PATCH 1/2] chore: submit coverage to codecov (#63) * chore: submit coverage to codecov * chore: add correct package name on gh action * chore: add windows to os matrix for tests action workflow --- .github/workflows/lint.yml | 2 +- .github/workflows/tests.yml | 56 +++++++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b36ef4c..252a382 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,6 +1,6 @@ name: Lint -on: [pull_request] +on: [push, pull_request] jobs: build: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 03f92d6..3373733 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,26 +1,52 @@ name: Tests -on: [pull_request] +on: [push, pull_request] jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: max-parallel: 4 matrix: python-version: ["3.6", "3.7", "3.8", "3.9-dev"] + os: [ubuntu-latest, windows-latest] + exclude: + - os: windows-latest + python-version: "3.6" + - os: windows-latest + python-version: "3.7" + - os: windows-latest + python-version: "3.9-dev" + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox tox-gh-actions + - name: Test with tox + run: tox + env: + TOXENV: ${{ matrix.toxenv }} + + coverage: + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install tox tox-gh-actions - - name: Test with tox - run: tox - env: - TOXENV: ${{ matrix.toxenv }} \ No newline at end of file + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Install test dependencies + run: | + python -m pip install --upgrade pip + pip install .[test] + - name: Test with coverage + run: pytest --cov=graphql_server --cov-report=xml tests + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v1 From e39398adae2a3b01ba4a33e271338d3e71c58c0a Mon Sep 17 00:00:00 2001 From: Russell Owen Date: Sat, 17 Oct 2020 13:28:15 -0700 Subject: [PATCH 2/2] Fix enable_async for aiohttp and sanic if graphiql is enabled (#67) * Fix enable_async=True in aiohttp Apply the fix suggested by ketanbshah in https://github.com/graphql-python/graphql-server/issues/64 * Apply the same fix to sanic * tests: add tests for graphiql enabled plus async Co-authored-by: Manuel Bojato <30560560+KingDarBoja@users.noreply.github.com> Co-authored-by: KingDarBoja --- graphql_server/aiohttp/graphqlview.py | 7 ++-- graphql_server/sanic/graphqlview.py | 9 +++-- tests/aiohttp/schema.py | 18 ++++++++++ tests/aiohttp/test_graphiqlview.py | 48 ++++++++++++++++++++++++--- tests/sanic/schema.py | 18 ++++++++++ tests/sanic/test_graphiqlview.py | 31 +++++++++++++++-- 6 files changed, 120 insertions(+), 11 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 ) 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 ) 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")