From c0f9f60ae9e126e4a808f60bacd041016e5538af Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 18 Oct 2018 15:24:26 +0100 Subject: [PATCH] Add graphiql support --- docs/datastructures.md | 0 docs/graphql.md | 4 +- starlette/graphql.py | 149 ++++++++++++++++++++++++++++++++++++++++- tests/test_graphql.py | 6 ++ 4 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 docs/datastructures.md diff --git a/docs/datastructures.md b/docs/datastructures.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/graphql.md b/docs/graphql.md index f1fd582a..c3e96e5f 100644 --- a/docs/graphql.md +++ b/docs/graphql.md @@ -20,7 +20,7 @@ schema = graphene.Schema(query=Query) app = Starlette() -app.add_route('/', GraphQLApp(schema=schema)) +app.add_route('/', GraphQLApp(schema=schema), methods=['GET', 'POST']) ``` ## Sync or Async executors @@ -51,5 +51,5 @@ schema = graphene.Schema(query=Query) app = Starlette() -app.add_route('/', GraphQLApp(schema=schema, executor=AsyncioExecutor())) +app.add_route('/', GraphQLApp(schema=schema, executor=AsyncioExecutor()), methods=['GET', 'POST']) ``` diff --git a/starlette/graphql.py b/starlette/graphql.py index 38996a84..acd35389 100644 --- a/starlette/graphql.py +++ b/starlette/graphql.py @@ -1,9 +1,10 @@ from starlette import status -from starlette.responses import PlainTextResponse, Response, JSONResponse +from starlette.responses import PlainTextResponse, Response, JSONResponse, HTMLResponse from starlette.requests import Request from starlette.types import ASGIInstance, Receive, Scope, Send import asyncio import functools +import json import typing try: @@ -29,11 +30,14 @@ class GraphQLApp: async def asgi(self, receive: Receive, send: Send, scope: Scope) -> None: request = Request(scope, receive=receive) - response = await self.handler(request) + response = await self.handle_graphql(request) await response(receive, send) - async def handler(self, request: Request) -> Response: + async def handle_graphql(self, request: Request) -> Response: if request.method == "GET": + if "text/html" in request.headers.get("Accept", ""): + return await self.handle_graphiql(request) + data = request.query_params # type: typing.Mapping[str, typing.Any] elif request.method == "POST": @@ -95,3 +99,142 @@ class GraphQLApp: ) loop = asyncio.get_event_loop() return await loop.run_in_executor(None, func, query) + + async def handle_graphiql(self, request: Request) -> Response: + text = GRAPHIQL.replace("{{REQUEST_PATH}}", json.dumps(request.url.path)) + return HTMLResponse(text) + + +GRAPHIQL = """ + + + + + + + + + + + + + +
Loading...
+ + + +""" diff --git a/tests/test_graphql.py b/tests/test_graphql.py index e4435b9e..a7b50b1e 100644 --- a/tests/test_graphql.py +++ b/tests/test_graphql.py @@ -74,6 +74,12 @@ def test_graphql_invalid_field(): } +def test_graphiql_get(): + response = client.get("/", headers={"accept": "text/html"}) + assert response.status_code == 200 + assert "" in response.text + + class ASyncQuery(graphene.ObjectType): hello = graphene.String(name=graphene.String(default_value="stranger"))