From 0bc97735368c23f84c8909283ccfacd66fb150b2 Mon Sep 17 00:00:00 2001 From: Jonathan Ehwald Date: Mon, 7 Oct 2024 19:41:18 +0200 Subject: [PATCH] Document how to render a custom graphql IDE (#3664) --- docs/integrations/aiohttp.md | 18 +++++++++++++ docs/integrations/asgi.md | 18 +++++++++++++ docs/integrations/chalice.md | 18 +++++++++++++ docs/integrations/channels.md | 21 +++++++++++++-- docs/integrations/django.md | 50 ++++++++++++++++++++++++++++++----- docs/integrations/fastapi.md | 17 ++++++++++++ docs/integrations/flask.md | 26 +++++++++++++++--- docs/integrations/litestar.md | 40 ++++++++++++++++++++++++++++ docs/integrations/quart.md | 26 +++++++++++++++--- docs/integrations/sanic.md | 19 +++++++++++++ strawberry/asgi/__init__.py | 2 +- 11 files changed, 238 insertions(+), 17 deletions(-) diff --git a/docs/integrations/aiohttp.md b/docs/integrations/aiohttp.md index 207f51b9b9..937bb31889 100644 --- a/docs/integrations/aiohttp.md +++ b/docs/integrations/aiohttp.md @@ -51,6 +51,7 @@ methods: - `async get_root_value(self, request: aiohttp.web.Request) -> object` - `async process_result(self, request: aiohttp.web.Request, result: ExecutionResult) -> GraphQLHTTPResponse` - `def encode_json(self, data: GraphQLHTTPResponse) -> str` +- `async def render_graphql_ide(self, request: aiohttp.web.Request) -> aiohttp.web.Response` ### get_context @@ -151,3 +152,20 @@ class MyGraphQLView(GraphQLView): def encode_json(self, data: GraphQLHTTPResponse) -> str: return json.dumps(data, indent=2) ``` + +### render_graphql_ide + +In case you need more control over the rendering of the GraphQL IDE than the +`graphql_ide` option provides, you can override the `render_graphql_ide` method. + +```python +from aiohttp import web +from strawberry.aiohttp.views import GraphQLView + + +class MyGraphQLView(GraphQLView): + async def render_graphql_ide(self, request: web.Request) -> web.Response: + custom_html = """

Custom GraphQL IDE

""" + + return web.Response(text=custom_html, content_type="text/html") +``` diff --git a/docs/integrations/asgi.md b/docs/integrations/asgi.md index 3c9d78bd50..1d5097e8d4 100644 --- a/docs/integrations/asgi.md +++ b/docs/integrations/asgi.md @@ -50,6 +50,7 @@ We allow to extend the base `GraphQL` app, by overriding the following methods: - `async get_root_value(self, request: Request) -> Any` - `async process_result(self, request: Request, result: ExecutionResult) -> GraphQLHTTPResponse` - `def encode_json(self, response_data: GraphQLHTTPResponse) -> str` +- `async def render_graphql_ide(self, request: Request) -> Response` ### get_context @@ -176,3 +177,20 @@ class MyGraphQLView(GraphQL): def encode_json(self, data: GraphQLHTTPResponse) -> str: return json.dumps(data, indent=2) ``` + +### render_graphql_ide + +In case you need more control over the rendering of the GraphQL IDE than the +`graphql_ide` option provides, you can override the `render_graphql_ide` method. + +```python +from strawberry.asgi import GraphQL +from starlette.responses import HTMLResponse, Response + + +class MyGraphQL(GraphQL): + async def render_graphql_ide(self, request: Request) -> Response: + custom_html = """

Custom GraphQL IDE

""" + + return HTMLResponse(custom_html) +``` diff --git a/docs/integrations/chalice.md b/docs/integrations/chalice.md index 5608eff948..04b8c2e23d 100644 --- a/docs/integrations/chalice.md +++ b/docs/integrations/chalice.md @@ -76,6 +76,7 @@ We allow to extend the base `GraphQLView`, by overriding the following methods: - `get_root_value(self, request: Request) -> Any` - `process_result(self, request: Request, result: ExecutionResult) -> GraphQLHTTPResponse` - `encode_json(self, response_data: GraphQLHTTPResponse) -> str` +- `def render_graphql_ide(self, request: Request) -> Response` ### get_context @@ -161,3 +162,20 @@ class MyGraphQLView(GraphQLView): def encode_json(self, data: GraphQLHTTPResponse) -> str: return json.dumps(data, indent=2) ``` + +### render_graphql_ide + +In case you need more control over the rendering of the GraphQL IDE than the +`graphql_ide` option provides, you can override the `render_graphql_ide` method. + +```python +from strawberry.chalice.views import GraphQLView +from chalice.app import Request, Response + + +class MyGraphQLView(GraphQLView): + def render_graphql_ide(self, request: Request) -> Response: + custom_html = """

Custom GraphQL IDE

""" + + return Response(custom_html, headers={"Content-Type": "text/html"}) +``` diff --git a/docs/integrations/channels.md b/docs/integrations/channels.md index 910e4a1ffc..af495a1ed9 100644 --- a/docs/integrations/channels.md +++ b/docs/integrations/channels.md @@ -533,9 +533,10 @@ We allow to extend `GraphQLHTTPConsumer`, by overriding the following methods: - `async def get_context(self, request: ChannelsRequest, response: TemporalResponse) -> Context` - `async def get_root_value(self, request: ChannelsRequest) -> Optional[RootValue]` -- `async def process_result(self, request: Request, result: ExecutionResult) -> GraphQLHTTPResponse:`. +- `async def process_result(self, request: Request, result: ExecutionResult) -> GraphQLHTTPResponse`. +- `async def render_graphql_ide(self, request: ChannelsRequest) -> ChannelsResponse` -### Context +#### Context The default context returned by `get_context()` is a `dict` that includes the following keys by default: @@ -552,6 +553,22 @@ following keys by default: errors (defaults to `200`) - `headers`: Any additional headers that should be send with the response +#### render_graphql_ide + +In case you need more control over the rendering of the GraphQL IDE than the +`graphql_ide` option provides, you can override the `render_graphql_ide` method. + +```python +from strawberry.channels import GraphQLHTTPConsumer, ChannelsRequest, ChannelsResponse + + +class MyGraphQLHTTPConsumer(GraphQLHTTPConsumer): + async def render_graphql_ide(self, request: ChannelsRequest) -> ChannelsResponse: + custom_html = """

Custom GraphQL IDE

""" + + return ChannelsResponse(content=custom_html, content_type="text/html") +``` + ## GraphQLWSConsumer (WebSockets / Subscriptions) ### Options diff --git a/docs/integrations/django.md b/docs/integrations/django.md index 281b40c34a..ee582c4797 100644 --- a/docs/integrations/django.md +++ b/docs/integrations/django.md @@ -62,11 +62,12 @@ encoding process. We allow to extend the base `GraphQLView`, by overriding the following methods: -- `get_context(self, request: HttpRequest, response: HttpResponse) -> Any` -- `get_root_value(self, request: HttpRequest) -> Any` -- `process_result(self, request: HttpRequest, result: ExecutionResult) -> GraphQLHTTPResponse` +- `def get_context(self, request: HttpRequest, response: HttpResponse) -> Any` +- `def get_root_value(self, request: HttpRequest) -> Any` +- `def process_result(self, request: HttpRequest, result: ExecutionResult) -> GraphQLHTTPResponse` +- `def render_graphql_ide(self, request: HttpRequest) -> HttpResponse` -## get_context +### get_context `get_context` allows to provide a custom context object that can be used in your resolver. You can return anything here, by default we return a @@ -101,7 +102,7 @@ called "example". Then we use the context in a resolver, the resolver will return "1" in this case. -## get_root_value +### get_root_value `get_root_value` allows to provide a custom root value for your schema, this is probably not used a lot but it might be useful in certain situations. @@ -122,7 +123,7 @@ class Query: Here we are returning a Query where the name is "Patrick", so we when requesting the field name we'll return "Patrick" in this case. -## process_result +### process_result `process_result` allows to customize and/or process results before they are sent to the clients. This can be useful logging errors or hiding them (for example to @@ -151,6 +152,24 @@ class MyGraphQLView(GraphQLView): In this case we are doing the default processing of the result, but it can be tweaked based on your needs. +### render_graphql_ide + +In case you need more control over the rendering of the GraphQL IDE than the +`graphql_ide` option provides, you can override the `render_graphql_ide` method. + +```python +from strawberry.django.views import GraphQLView +from django.http import HttpResponse +from django.template.loader import render_to_string + + +class MyGraphQLView(GraphQLView): + def render_graphql_ide(self, request: HttpRequest) -> HttpResponse: + content = render_to_string("myapp/my_graphql_ide_template.html") + + return HttpResponse(content) +``` + # Async Django Strawberry also provides an async view that you can use with Django 3.1+ @@ -190,6 +209,7 @@ methods: - `async get_root_value(self, request: HttpRequest) -> Any` - `async process_result(self, request: HttpRequest, result: ExecutionResult) -> GraphQLHTTPResponse` - `def encode_json(self, data: GraphQLHTTPResponse) -> str` +- `async def render_graphql_ide(self, request: HttpRequest) -> HttpResponse` ### get_context @@ -277,6 +297,24 @@ class MyGraphQLView(AsyncGraphQLView): return json.dumps(data, indent=2) ``` +### render_graphql_ide + +In case you need more control over the rendering of the GraphQL IDE than the +`graphql_ide` option provides, you can override the `render_graphql_ide` method. + +```python +from strawberry.django.views import AsyncGraphQLView +from django.http import HttpResponse +from django.template.loader import render_to_string + + +class MyGraphQLView(AsyncGraphQLView): + async def render_graphql_ide(self, request: HttpRequest) -> HttpResponse: + content = render_to_string("myapp/my_graphql_ide_template.html") + + return HttpResponse(content) +``` + ## Subscriptions Subscriptions run over websockets and thus depend on diff --git a/docs/integrations/fastapi.md b/docs/integrations/fastapi.md index 4c25ca651b..f9ffc1de3f 100644 --- a/docs/integrations/fastapi.md +++ b/docs/integrations/fastapi.md @@ -301,3 +301,20 @@ class MyGraphQLRouter(GraphQLRouter): def encode_json(self, data: GraphQLHTTPResponse) -> bytes: return orjson.dumps(data) ``` + +### render_graphql_ide + +In case you need more control over the rendering of the GraphQL IDE than the +`graphql_ide` option provides, you can override the `render_graphql_ide` method. + +```python +from strawberry.fastapi import GraphQLRouter +from starlette.responses import HTMLResponse, Response + + +class MyGraphQLRouter(GraphQLRouter): + async def render_graphql_ide(self, request: Request) -> HTMLResponse: + custom_html = """

Custom GraphQL IDE

""" + + return HTMLResponse(custom_html) +``` diff --git a/docs/integrations/flask.md b/docs/integrations/flask.md index 69576e535c..a8dde13b8a 100644 --- a/docs/integrations/flask.md +++ b/docs/integrations/flask.md @@ -51,10 +51,11 @@ The `GraphQLView` accepts the following options at the moment: We allow to extend the base `GraphQLView`, by overriding the following methods: -- `get_context(self, request: Request, response: Response) -> Any` -- `get_root_value(self, request: Request) -> Any` -- `process_result(self, result: ExecutionResult) -> GraphQLHTTPResponse` -- `encode_json(self, response_data: GraphQLHTTPResponse) -> str` +- `def get_context(self, request: Request, response: Response) -> Any` +- `def get_root_value(self, request: Request) -> Any` +- `def process_result(self, result: ExecutionResult) -> GraphQLHTTPResponse` +- `def encode_json(self, response_data: GraphQLHTTPResponse) -> str` +- `def render_graphql_ide(self, request: Request) -> Response` @@ -148,3 +149,20 @@ class MyGraphQLView(GraphQLView): def encode_json(self, data: GraphQLHTTPResponse) -> str: return json.dumps(data, indent=2) ``` + +### render_graphql_ide + +In case you need more control over the rendering of the GraphQL IDE than the +`graphql_ide` option provides, you can override the `render_graphql_ide` method. + +```python +from strawberry.flask.views import GraphQLView +from flask import Request, Response + + +class MyGraphQLView(GraphQLView): + def render_graphql_ide(self, request: Request) -> Response: + custom_html = """

Custom GraphQL IDE

""" + + return Response(custom_html, status=200, content_type="text/html") +``` diff --git a/docs/integrations/litestar.md b/docs/integrations/litestar.md index 54ea4e239d..86ee0bc179 100644 --- a/docs/integrations/litestar.md +++ b/docs/integrations/litestar.md @@ -317,3 +317,43 @@ GraphQLController = make_graphql_controller( app = Litestar(route_handlers=[GraphQLController]) ``` + +## Extending the controller + +The `make_graphql_controller` function returns a `GraphQLController` class that +can be extended by overriding the following methods: + +1. `async def render_graphql_ide(self, request: Request) -> Response` + +### render_graphql_ide + +In case you need more control over the rendering of the GraphQL IDE than the +`graphql_ide` option provides, you can override the `render_graphql_ide` method. + +```python +import strawberry +from strawberry.litestar import make_graphql_controller +from litestar import MediaType, Request, Response + + +@strawberry.type +class Query: + @strawberry.field + def hello(self) -> str: + return "world" + + +schema = strawberry.Schema(Query) + +GraphQLController = make_graphql_controller( + schema, + path="/graphql", +) + + +class MyGraphQLController(GraphQLController): + async def render_graphql_ide(self, request: Request) -> Response: + custom_html = """

Custom GraphQL IDE

""" + + return Response(custom_html, media_type=MediaType.HTML) +``` diff --git a/docs/integrations/quart.md b/docs/integrations/quart.md index 91b8b2a934..d863777b04 100644 --- a/docs/integrations/quart.md +++ b/docs/integrations/quart.md @@ -43,10 +43,11 @@ The `GraphQLView` accepts the following options at the moment: We allow to extend the base `GraphQLView`, by overriding the following methods: -- `get_context(self, request: Request, response: Response) -> Any` -- `get_root_value(self, request: Request) -> Any` -- `process_result(self, result: ExecutionResult) -> GraphQLHTTPResponse` -- `encode_json(self, response_data: GraphQLHTTPResponse) -> str` +- `async def get_context(self, request: Request, response: Response) -> Any` +- `async def get_root_value(self, request: Request) -> Any` +- `async def process_result(self, result: ExecutionResult) -> GraphQLHTTPResponse` +- `def encode_json(self, response_data: GraphQLHTTPResponse) -> str` +- `async def render_graphql_ide(self, request: Request) -> Response` ### get_context @@ -132,3 +133,20 @@ class MyGraphQLView(GraphQLView): def encode_json(self, data: GraphQLHTTPResponse) -> str: return json.dumps(data, indent=2) ``` + +### render_graphql_ide + +In case you need more control over the rendering of the GraphQL IDE than the +`graphql_ide` option provides, you can override the `render_graphql_ide` method. + +```python +from strawberry.quart.views import GraphQLView +from quart import Request, Response + + +class MyGraphQLView(GraphQLView): + async def render_graphql_ide(self, request: Request) -> Response: + custom_html = """

Custom GraphQL IDE

""" + + return Response(self.graphql_ide_html) +``` diff --git a/docs/integrations/sanic.md b/docs/integrations/sanic.md index dfff4c0502..2cae238b30 100644 --- a/docs/integrations/sanic.md +++ b/docs/integrations/sanic.md @@ -43,6 +43,7 @@ methods: - `async get_context(self, request: Request, response: Response) -> Any` - `async get_root_value(self, request: Request) -> Any` - `async process_result(self, result: ExecutionResult) -> GraphQLHTTPResponse` +- `async def render_graphql_ide(self, request: Request) -> HTTPResponse` ### get_context @@ -130,3 +131,21 @@ class MyGraphQLView(GraphQLView): def encode_json(self, data: GraphQLHTTPResponse) -> str: return json.dumps(data, indent=2) ``` + +### render_graphql_ide + +In case you need more control over the rendering of the GraphQL IDE than the +`graphql_ide` option provides, you can override the `render_graphql_ide` method. + +```python +from strawberry.sanic.views import GraphQLView +from sanic.request import Request +from sanic.response import HTTPResponse, html + + +class MyGraphQLView(GraphQLView): + async def render_graphql_ide(self, request: Request) -> HTTPResponse: + custom_html = """

Custom GraphQL IDE

""" + + return html(custom_html) +``` diff --git a/strawberry/asgi/__init__.py b/strawberry/asgi/__init__.py index d10d207987..5a3f01203d 100644 --- a/strawberry/asgi/__init__.py +++ b/strawberry/asgi/__init__.py @@ -186,7 +186,7 @@ async def get_sub_response( return sub_response - async def render_graphql_ide(self, request: Union[Request, WebSocket]) -> Response: + async def render_graphql_ide(self, request: Request) -> Response: return HTMLResponse(self.graphql_ide_html) def create_response(