From 72574504ec5bdede87f814d4167318fe8b3606ee Mon Sep 17 00:00:00 2001 From: rafalp Date: Thu, 19 Dec 2024 14:56:01 +0100 Subject: [PATCH] Ariadne 0.24 docs and release notes --- docs/asgi-handlers-reference.md | 6 +- docs/constants-reference.md | 30 - docs/exceptions-reference.md | 31 +- docs/explorers.md | 2 + website/blog/2024-12-19-ariadne-0-24.md | 51 + .../version-0.24/asgi-handlers-reference.md | 1198 +++++++++++++++++ .../version-0.24/constants-reference.md | 55 + .../version-0.24/exceptions-reference.md | 158 +++ .../versioned_docs/version-0.24/explorers.md | 227 ++++ 9 files changed, 1720 insertions(+), 38 deletions(-) create mode 100644 website/blog/2024-12-19-ariadne-0-24.md create mode 100644 website/versioned_docs/version-0.24/asgi-handlers-reference.md create mode 100644 website/versioned_docs/version-0.24/constants-reference.md create mode 100644 website/versioned_docs/version-0.24/exceptions-reference.md create mode 100644 website/versioned_docs/version-0.24/explorers.md diff --git a/docs/asgi-handlers-reference.md b/docs/asgi-handlers-reference.md index 94dc05f5..fd9ef5c3 100644 --- a/docs/asgi-handlers-reference.md +++ b/docs/asgi-handlers-reference.md @@ -164,13 +164,13 @@ error message and 400 status code is returned instead. #### `extract_data_from_request` ```python -async def extract_data_from_request(self, request: Request) -> None: +async def extract_data_from_request(self, request: Request) -> Union[dict, list]: ... ``` Extracts GraphQL request data from request. -Returns a `dict` with GraphQL query data that was not yet validated. +Returns a `dict` or `list` with GraphQL query data that was not yet validated. ##### Required arguments @@ -207,7 +207,7 @@ async def extract_data_from_multipart_request( Extracts GraphQL data from `multipart/form-data` request. -Returns an unvalidated `dict` with GraphQL query data. +Returns an unvalidated `dict` or `list` with GraphQL query data. ##### Required arguments diff --git a/docs/constants-reference.md b/docs/constants-reference.md index 002ac1ab..a722607f 100644 --- a/docs/constants-reference.md +++ b/docs/constants-reference.md @@ -51,34 +51,4 @@ DATA_TYPE_JSON = 'application/json' ```python DATA_TYPE_MULTIPART = 'multipart/form-data' -``` - - -- - - - - - - -## `HTTP_STATUS_200_OK` - -```python -HTTP_STATUS_200_OK = '200 OK' -``` - - -- - - - - - - -## `HTTP_STATUS_400_BAD_REQUEST` - -```python -HTTP_STATUS_400_BAD_REQUEST = '400 Bad Request' -``` - - -- - - - - - - -## `HTTP_STATUS_405_METHOD_NOT_ALLOWED` - -```python -HTTP_STATUS_405_METHOD_NOT_ALLOWED = '405 Method Not Allowed' ``` \ No newline at end of file diff --git a/docs/exceptions-reference.md b/docs/exceptions-reference.md index b0b20a3e..f0238ccc 100644 --- a/docs/exceptions-reference.md +++ b/docs/exceptions-reference.md @@ -86,6 +86,16 @@ Raised when request did not contain the data required to execute the GraphQL query. +### Constructor + +```python +def __init__(self, message: Optional[str] = None): + ... +``` + +Initializes the `HttpBadRequestError` with optional error message. + + - - - - - @@ -102,17 +112,28 @@ Base class for HTTP errors raised inside the ASGI and WSGI servers. ### Constructor ```python -def __init__(self, message: Optional[str] = None): +def __init__(self, status: str, message: Optional[str] = None): ... ``` -Initializes the `HttpError` with optional error message. +Initializes the `HttpError` with a status and optional error message. -#### Optional arguments +#### Arguments -`message`: a `str` with error message to return in response body or -`None`. +`status`: HTTP status code as `HttpStatusResponse`. +`message`: Optional error message to return in the response body. + + +- - - - - + + +## `HttpStatusResponse` + +```python +class HttpStatusResponse(Enum): + ... +``` - - - - - diff --git a/docs/explorers.md b/docs/explorers.md index a03fe0ea..310a939e 100644 --- a/docs/explorers.md +++ b/docs/explorers.md @@ -42,6 +42,7 @@ Embedded Apollo Sandbox. - `title: str = "Ariadne GraphQL"` - Used for page title and loading message. - `default_query: str = "..."` - Default content of editor area. +- `include_cookies: bool: bool = False` - Controls if Apollo explorer should include cookies in requests. ## GraphQL Playground @@ -58,6 +59,7 @@ GraphQL Playground was default explorer in Ariadne until 0.17 release. **It's no `ExplorerPlayground` constructor accepts following options: - `title: str = "Ariadne GraphQL"` - Used for page title and loading message. +- `share_enabled: bool = False` - Controls share playground feature. - `editor_cursor_shape: Optional[str] = None` - Controls `editor.cursorShape` setting (defaults to `"line"`, can be changed to `"block"` or `"underline"`). - `editor_font_family: Optional[str] = None` - Controls `editor.fontFamily` setting (defaults to `"'Source Code Pro', 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace"`). - `editor_font_size: Optional[int] = None` - Controls `editor.fontSize` setting (defaults to `14`). diff --git a/website/blog/2024-12-19-ariadne-0-24.md b/website/blog/2024-12-19-ariadne-0-24.md new file mode 100644 index 00000000..8e7db9fc --- /dev/null +++ b/website/blog/2024-12-19-ariadne-0-24.md @@ -0,0 +1,51 @@ +--- +title: Ariadne 0.24 +--- + +Ariadne 0.24 is now available. + +Ariadne 0.24 is a maintenance release that implements improvements and fixes to reported issues. + + + +## Added validation for directive declarations in `make_executable_schema` to prevent schema creation with undeclared directives + +`SchemaDirectiveVisitor` that `make_executable_schema` uses for GraphQL directives will now raise the `ValueError` if directive is not declared in the GraphQL schema. + + +## Added `include_cookies` option to the `ExplorerApollo` + +Apollo Explorer's `includeCookies` option can now be enabled in Ariadne via the `include_cookies` kwarg. + + +## Added `share_enabled` param to `ExplorerPlayground` to enable share playground feature + +GraphQL Playground's `shareEnabled` option can now be enabled in Ariadne via the `share_enabled` kwarg. + + +## Added support for nested attribute resolution in alias resolvers + +Ariadne's `resolve_to` utility now supports data structure traversal in created resolvers when `.` is used in the `attr_name` argument: + +```python +resolver = resolve_to("attr.child_attr.deeper_child_attr") +``` + + +## Replaced regexes in the Apollo Federation implementation with cleaner approach using GraphQL AST + +Ariadne used series of regexes to process GraphQL schemas used for Apollo Federation. This approach was prone to errors and edge cases. + +In 0.24 this approach was replaced with new one that instead parses schema to AST. + + +## CHANGELOG + +- Added validation for directive declarations in `make_executable_schema` to prevent schema creation with undeclared directives. +- Replaced hardcoded HTTP statuses with `HTTPStatus` from the `http` stdlib module. +- Added `include_cookies` option to the `ExplorerApollo`. +- Fixed typing on `extract_data_from_request` method. +- Fixed tests websockets after starlette update. +- Added `share_enabled` param to `ExplorerPlayground` to enable share playground feature. +- Added support for nested attribute resolution in alias resolvers. +- Replaced regexes in the Apollo Federation implementation with cleaner approach using GraphQL AST. \ No newline at end of file diff --git a/website/versioned_docs/version-0.24/asgi-handlers-reference.md b/website/versioned_docs/version-0.24/asgi-handlers-reference.md new file mode 100644 index 00000000..3141ffe8 --- /dev/null +++ b/website/versioned_docs/version-0.24/asgi-handlers-reference.md @@ -0,0 +1,1198 @@ +--- +id: version-0.24-asgi-handlers-reference +title: ASGI handlers reference +sidebar_label: ariadne.asgi.handlers +original_id: asgi-handlers-reference +--- + +The `ariadne.asgi.handlers` package exports following +ASGI request handlers: + + + +## `GraphQLHTTPHandler` + +```python +class GraphQLHTTPHandler(GraphQLHttpHandlerBase): + ... +``` + +Default ASGI handler for HTTP requests. + +Supports the `Query` and `Mutation` operations. + + +### Constructor + +```python +def __init__( + self, + extensions: Optional[Extensions] = None, + middleware: Optional[Middlewares] = None, + middleware_manager_class: Optional[Type[MiddlewareManager]] = None, +): + ... +``` + +Initializes the HTTP handler. + + +#### Optional arguments + +[`extensions`](types-reference.md#extensions): an [`Extensions`](types-reference.md#extensions) list or callable returning a +list of extensions server should use during query execution. Defaults +to no extensions. + +`middleware`: a [`Middlewares`](types-reference.md#middlewares) list or callable returning a list of +middlewares server should use during query execution. Defaults to no +middlewares. + +`middleware_manager_class`: a `MiddlewareManager` type or subclass to +use for combining provided middlewares into single wrapper for resolvers +by the server. Defaults to `graphql.MiddlewareManager`. Is only used +if [`extensions`](types-reference.md#extensions) or `middleware` options are set. + + +### Methods + +#### `handle` + +```python +async def handle(self, scope: Scope, receive: Receive, send: Send) -> None: + ... +``` + +An entrypoint for the GraphQL HTTP handler. + +This method is called by the Ariadne ASGI GraphQL application to execute +queries done using the HTTP protocol. + +It creates the `starlette.requests.Request` instance, calls +`handle_request` method with it, then sends response back to the client. + + +##### Required arguments + +`scope`: The [connection scope](https://asgi.readthedocs.io/en/latest/specs/main.html#connection-scope) information, a dictionary that contains +at least a type key specifying the protocol that is incoming. + +`receive`: an awaitable callable that will yield a new event dictionary +when one is available. + +`send`: an awaitable callable taking a single event dictionary as a +positional argument that will return once the send has been completed +or the connection has been closed. + +Details about the arguments and their usage are described in the +ASGI specification: + +https://asgi.readthedocs.io/en/latest/specs/main.html + + +#### `handle_request` + +```python +async def handle_request(self, request: Request) -> Response: + ... +``` + +Handle GraphQL request and return response for the client. + +Is called by the `handle` method and `handle_request` method of the +ASGI GraphQL application. + +Handles three HTTP methods: + +`GET`: returns GraphQL explorer or 405 error response if explorer or +introspection is disabled. + +`POST`: executes the GraphQL query from either `application/json` or +`multipart/form-data` requests. + +`OPTIONS`: returns supported HTTP methods. + +Returns Starlette's `Response` instance, which is also works in FastAPI. + + +##### Required arguments: + +`request`: the `Request` instance from Starlette or FastAPI. + + +#### `render_explorer` + +```python +async def render_explorer(self, request: Request, explorer: Explorer) -> Response: + ... +``` + +Return a HTML response with GraphQL explorer. + + +##### Required arguments: + +`request`: the `Request` instance from Starlette or FastAPI. + +[`explorer`](explorers.md): an [`Explorer`](explorers.md) instance that implements the +`html(request: Request)` method which returns either the `str` with HTML +or `None`. If explorer returns `None`, `405` method not allowed response +is returned instead. + + +#### `graphql_http_server` + +```python +async def graphql_http_server(self, request: Request) -> Response: + ... +``` + +Handles the HTTP request with GraphQL query. + +Extracts GraphQL query data from requests and then executes it using +the `execute_graphql_query` method. + +Returns the JSON response from Sta + +If request's data was invalid or missing, plaintext response with +error message and 400 status code is returned instead. + + +##### Required arguments: + +`request`: the `Request` instance from Starlette or FastAPI. + + +#### `extract_data_from_request` + +```python +async def extract_data_from_request(self, request: Request) -> Union[dict, list]: + ... +``` + +Extracts GraphQL request data from request. + +Returns a `dict` or `list` with GraphQL query data that was not yet validated. + + +##### Required arguments + +`request`: the `Request` instance from Starlette or FastAPI. + + +#### `extract_data_from_json_request` + +```python +async def extract_data_from_json_request(self, request: Request) -> dict: + ... +``` + +Extracts GraphQL data from JSON request. + +Returns a `dict` with GraphQL query data that was not yet validated. + + +##### Required arguments + +`request`: the `Request` instance from Starlette or FastAPI. + + +#### `extract_data_from_multipart_request` + +```python +async def extract_data_from_multipart_request( + self, + request: Request, +) -> Union[dict, list]: + ... +``` + +Extracts GraphQL data from `multipart/form-data` request. + +Returns an unvalidated `dict` or `list` with GraphQL query data. + + +##### Required arguments + +`request`: the `Request` instance from Starlette or FastAPI. + + +#### `extract_data_from_get_request` + +```python +def extract_data_from_get_request(self, request: Request) -> dict: + ... +``` + +Extracts GraphQL data from GET request's querystring. + +Returns a `dict` with GraphQL query data that was not yet validated. + + +##### Required arguments + +`request`: the `Request` instance from Starlette or FastAPI. + + +#### `execute_graphql_query` + +```python +async def execute_graphql_query( + self, + request: Any, + data: Any, + *, + context_value: Any, + query_document: Optional[DocumentNode], +) -> GraphQLResult: + ... +``` + +Executes GraphQL query from `request` and returns [`GraphQLResult`](types-reference.md#graphqlresult). + +Creates GraphQL [`ContextValue`](types-reference.md#contextvalue), initializes extensions and middlewares, +then runs the `graphql` function from Ariadne to execute the query. + + +##### Requires arguments + +`request`: the `Request` instance from Starlette or FastAPI. + +`data`: a GraphQL data. + + +##### Optional arguments + +`context_value`: a [`ContextValue`](types-reference.md#contextvalue) for this request. + +`query_document`: an already parsed GraphQL query. Setting this option +will prevent `graphql` from parsing `query` string from `data` second time. + + +#### `get_extensions_for_request` + +```python +async def get_extensions_for_request( + self, + request: Any, + context: Optional[ContextValue], +) -> ExtensionList: + ... +``` + +Returns extensions to use when handling the GraphQL request. + +Returns [`ExtensionList`](types-reference.md#extensionlist), a list of extensions to use or `None`. + + +##### Required arguments + +`request`: the `Request` instance from Starlette or FastAPI. + +`context`: a [`ContextValue`](types-reference.md#contextvalue) for this request. + + +#### `get_middleware_for_request` + +```python +async def get_middleware_for_request( + self, + request: Any, + context: Optional[ContextValue], +) -> MiddlewareList: + ... +``` + +Returns GraphQL middlewares to use when handling the GraphQL request. + +Returns [`MiddlewareList`](types-reference.md#middlewarelist), a list of middlewares to use or `None`. + + +##### Required arguments + +`request`: the `Request` instance from Starlette or FastAPI. + +`context`: a [`ContextValue`](types-reference.md#contextvalue) for this request. + + +#### `create_json_response` + +```python +async def create_json_response( + self, + request: Request, + result: dict, + success: bool, +) -> Response: + ... +``` + +Creates JSON response from GraphQL's query result. + +Returns Starlette's `JSONResponse` instance that's also compatible +with FastAPI. If `success` is `True`, response's status code is 200. +Status code 400 is used otherwise. + + +##### Required arguments + +`request`: the `Request` instance from Starlette or FastAPI. + +`result`: a JSON-serializable `dict` with query result. + +`success`: a `bool` specifying if + + +#### `handle_not_allowed_method` + +```python +def handle_not_allowed_method(self, request: Request) -> None: + ... +``` + +Handles request for unsupported HTTP method. + +Returns 200 response for `OPTIONS` request and 405 response for other +methods. All responses have empty body. + + +##### Required arguments + +`request`: the `Request` instance from Starlette or FastAPI. + + +- - - - - + + +## `GraphQLHandler` + +```python +class GraphQLHandler(ABC): + ... +``` + +Base class for ASGI connection handlers. + + +### Constructor + +```python +def __init__(self): + ... +``` + +Initialize the handler instance with empty configuration. + + +### Methods + +#### `handle` + +```python +async def handle(self, scope: Scope, receive: Receive, send: Send) -> None: + ... +``` + +An entrypoint for the ASGI connection handler. + +This method is called by Ariadne ASGI GraphQL application. Subclasses +should replace it with custom implementation. + + +##### Required arguments + +`scope`: The [connection scope](https://asgi.readthedocs.io/en/latest/specs/main.html#connection-scope) information, a dictionary that contains +at least a type key specifying the protocol that is incoming. + +`receive`: an awaitable callable that will yield a new event dictionary +when one is available. + +`send`: an awaitable callable taking a single event dictionary as a +positional argument that will return once the send has been completed +or the connection has been closed. + +Details about the arguments and their usage are described in the +ASGI specification: + +https://asgi.readthedocs.io/en/latest/specs/main.html + + +#### `configure` + +```python +def configure( + self, + schema: GraphQLSchema, + context_value: Optional[ContextValue] = None, + root_value: Optional[RootValue] = None, + query_parser: Optional[QueryParser] = None, + query_validator: Optional[QueryValidator] = None, + validation_rules: Optional[ValidationRules] = None, + execute_get_queries: bool = False, + debug: bool = False, + introspection: bool = True, + explorer: Optional[Explorer] = None, + logger: Union[None, str, Logger, LoggerAdapter] = None, + error_formatter: ErrorFormatter = format_error, + execution_context_class: Optional[Type[ExecutionContext]] = None, +) -> None: + ... +``` + +Configures the handler with options from the ASGI application. + +Called by Ariadne ASGI GraphQL application as part of its +initialization, propagating the configuration to it's handlers. + + +#### `get_context_for_request` + +```python +async def get_context_for_request(self, request: Any, data: dict) -> Any: + ... +``` + +Returns GraphQL context value for ASGI connection. + +Resolves final context value from the [`ContextValue`](types-reference.md#contextvalue) value passed to +`context_value` option. If `context_value` is None, sets default context +value instead, which is a `dict` with single `request` key that contains +either `starlette.requests.Request` instance or +`starlette.websockets.WebSocket` instance. + + +##### Required arguments + +`request`: an instance of ASGI connection. It's type depends on handler. + +`data`: a GraphQL data from connection. + + +- - - - - + + +## `GraphQLHttpHandlerBase` + +```python +class GraphQLHttpHandlerBase(GraphQLHandler): + ... +``` + +Base class for ASGI HTTP connection handlers. + + +### Methods + +#### `handle_request` + +```python +async def handle_request(self, request: Any) -> Any: + ... +``` + +Abstract method for handling the request. + +Should return valid ASGI response. + + +#### `execute_graphql_query` + +```python +async def execute_graphql_query( + self, + request: Any, + data: Any, + *, + context_value: Optional[Any], + query_document: Optional[DocumentNode], +) -> GraphQLResult: + ... +``` + +Abstract method for GraphQL query execution. + + +- - - - - + + +## `GraphQLTransportWSHandler` + +```python +class GraphQLTransportWSHandler(GraphQLWebsocketHandler): + ... +``` + +Implementation of the (newer) graphql-transport-ws subprotocol +from the graphql-ws library. + +For more details see it's GH page: + +https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md + + +### Constructor + +```python +def __init__( + self, + *args, + connection_init_wait_timeout: timedelta, + **kwargs, +): + ... +``` + +Initializes the websocket handler. + + +#### Optional arguments + +`connection_init_wait_timeout`: a `timedelta` with timeout for new +websocket connections before first message is received. Defaults to +60 seconds. + + +### Methods + +#### `handle` + +```python +async def handle(self, scope: Scope, receive: Receive, send: Send) -> None: + ... +``` + +An entrypoint for the GraphQL WebSocket handler. + +This method is called by the Ariadne ASGI GraphQL application to handle +the websocket connections. + +It creates the `starlette.websockets.WebSocket` instance and calls +`handle_websocket` method with it. + + +##### Required arguments + +`scope`: The [connection scope](https://asgi.readthedocs.io/en/latest/specs/main.html#connection-scope) information, a dictionary that contains +at least a type key specifying the protocol that is incoming. + +`receive`: an awaitable callable that will yield a new event dictionary +when one is available. + +`send`: an awaitable callable taking a single event dictionary as a +positional argument that will return once the send has been completed +or the connection has been closed. + +Details about the arguments and their usage are described in the +ASGI specification: + +https://asgi.readthedocs.io/en/latest/specs/main.html + + +#### `handle_websocket` + +```python +async def handle_websocket(self, websocket: WebSocket) -> None: + ... +``` + +Handle GraphQL the WebSocket connection. + +Is called by the `handle` method and `handle_websocket` method of the +ASGI GraphQL application. + + +##### Required arguments: + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + + +#### `handle_connection_init_timeout` + +```python +async def handle_connection_init_timeout( + self, + websocket: WebSocket, + client_context: ClientContext, +) -> None: + ... +``` + + +#### `handle_websocket_message` + +```python +async def handle_websocket_message( + self, + websocket: WebSocket, + message: dict, + client_context: ClientContext, +) -> None: + ... +``` + +Handles new message from websocket connection. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + +`message`: a `dict` with message payload. + +`client_context`: a `ClientContext` object with extra state of current +websocket connection. + + +#### `handle_websocket_connection_init_message` + +```python +async def handle_websocket_connection_init_message( + self, + websocket: WebSocket, + message: dict, + client_context: ClientContext, +) -> None: + ... +``` + +Handles `connection_init` websocket message. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + +`message`: a `dict` with message payload. + +`client_context`: a `ClientContext` object with extra state of current +websocket connection. + + +#### `handle_websocket_ping_message` + +```python +async def handle_websocket_ping_message( + self, + websocket: WebSocket, + client_context: ClientContext, +) -> None: + ... +``` + +Handles `ping` websocket message, answering with `pong` message. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + +`client_context`: a `ClientContext` object with extra state of current +websocket connection. + + +#### `handle_websocket_pong_message` + +```python +async def handle_websocket_pong_message( + self, + websocket: WebSocket, + client_context: ClientContext, +) -> None: + ... +``` + +Handles `pong` websocket message. + +Unlike `ping` message, `pong` is unidirectional heartbeat sent by the +client to the server. It doesn't require a result. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + +`client_context`: a `ClientContext` object with extra state of current +websocket connection. + + +#### `handle_websocket_complete_message` + +```python +async def handle_websocket_complete_message( + self, + websocket: WebSocket, + operation_id: str, + client_context: ClientContext, +) -> None: + ... +``` + +Handles `complete` websocket message. + +`complete` message tells the GraphQL server to stop sending events for +GraphQL operation specified in the message + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + +`operation_id`: a `str` with id of operation that should be stopped. + +`client_context`: a `ClientContext` object with extra state of current +websocket connection. + + +#### `handle_websocket_subscribe` + +```python +async def handle_websocket_subscribe( + self, + websocket: WebSocket, + data: Any, + operation_id: str, + client_context: ClientContext, +) -> None: + ... +``` + +Handles `subscribe` websocket message. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + +`data`: any data from `subscribe` message. + +`operation_id`: a `str` with id of new subscribe operation. + +`client_context`: a `ClientContext` object with extra state of current +websocket connection. + + +#### `handle_websocket_invalid_type` + +```python +async def handle_websocket_invalid_type(self, websocket: WebSocket) -> None: + ... +``` + +Handles unsupported or invalid websocket message. + +Closes open websocket connection with error code `4400`. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + + +#### `handle_on_complete` + +```python +async def handle_on_complete( + self, + websocket: WebSocket, + operation: Operation, +) -> None: + ... +``` + +Handles completed websocket operation. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + +[`operation`](types-reference.md#operation): a completed [`Operation`](types-reference.md#operation). + + +#### `stop_websocket_operation` + +```python +async def stop_websocket_operation( + self, + websocket: WebSocket, + operation_id: str, + client_context: ClientContext, +) -> None: + ... +``` + +Stops specified GraphQL operation for given connection and context. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + +`operation_id`: a `str` with id of operation to stop. + +`client_context`: a `ClientContext` object with extra state of current +websocket connection. + + +#### `observe_async_results` + +```python +async def observe_async_results( + self, + websocket: WebSocket, + results_producer: AsyncGenerator, + operation_id: str, + client_context: ClientContext, +) -> None: + ... +``` + +Converts results from Ariadne's `subscribe` generator into websocket +messages it next sends to the client. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + +`results_producer`: the `AsyncGenerator` returned from Ariadne's +`subscribe` function. + +`operation_id`: a `str` with id of operation. + +`client_context`: a `ClientContext` object with extra state of current +websocket connection. + + +- - - - - + + +## `GraphQLWSHandler` + +```python +class GraphQLWSHandler(GraphQLWebsocketHandler): + ... +``` + +Implementation of the (older) graphql-ws subprotocol from the +subscriptions-transport-ws library. + +For more details see it's GH page: + +https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md + + +### Constructor + +```python +def __init__(self, *args, keepalive: Optional[float], **kwargs): + ... +``` + +Initializes the websocket handler. + + +#### Optional arguments + +`keepalive`: a `float` with time frequency for sending the keep-alive +messages to clients with open websocket connections. `1.0` is 1 second. +If set to `None` or `0`, no keep-alive messages are sent. +Defaults to `None`. + + +### Methods + +#### `handle` + +```python +async def handle(self, scope: Scope, receive: Receive, send: Send) -> None: + ... +``` + +An entrypoint for the GraphQL WebSocket handler. + +This method is called by the Ariadne ASGI GraphQL application to handle +the websocket connections. + +It creates the `starlette.websockets.WebSocket` instance and calls +`handle_websocket` method with it. + + +##### Required arguments + +`scope`: The [connection scope](https://asgi.readthedocs.io/en/latest/specs/main.html#connection-scope) information, a dictionary that contains +at least a type key specifying the protocol that is incoming. + +`receive`: an awaitable callable that will yield a new event dictionary +when one is available. + +`send`: an awaitable callable taking a single event dictionary as a +positional argument that will return once the send has been completed +or the connection has been closed. + +Details about the arguments and their usage are described in the +ASGI specification: + +https://asgi.readthedocs.io/en/latest/specs/main.html + + +#### `handle_websocket` + +```python +async def handle_websocket(self, websocket: WebSocket) -> None: + ... +``` + +Handle GraphQL the WebSocket connection. + +Is called by the `handle` method and `handle_websocket` method of the +ASGI GraphQL application. + + +##### Required arguments: + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + + +#### `handle_websocket_message` + +```python +async def handle_websocket_message( + self, + websocket: WebSocket, + message: dict, + operations: Dict[str, Operation], +) -> None: + ... +``` + +Handles new message from websocket connection. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + +`message`: a `dict` with message payload. + +`operations`: a `dict` with currently active GraphQL operations. + + +#### `process_single_message` + +```python +async def process_single_message( + self, + websocket: WebSocket, + data: Any, + operation_id: str, + operations: Dict[str, Operation], +) -> None: + ... +``` + +Processes websocket message containing new GraphQL operation. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + +`data`: a `dict` with message's payload. + +`operation_id`: a `str` with an ID of new operation. + +`operations`: a `dict` with currently active GraphQL operations. + + +#### `handle_websocket_connection_init_message` + +```python +async def handle_websocket_connection_init_message( + self, + websocket: WebSocket, + message: dict, +) -> None: + ... +``` + +Handles `connection_init` websocket message. + +Initializes new websocket instance. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + +`message`: a `dict` with message's payload. + + +#### `handle_websocket_connection_terminate_message` + +```python +async def handle_websocket_connection_terminate_message( + self, + websocket: WebSocket, +) -> None: + ... +``` + +Handles `terminate` websocket message. + +Closes open websocket connection. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + + +#### `keep_websocket_alive` + +```python +async def keep_websocket_alive(self, websocket: WebSocket) -> None: + ... +``` + + +#### `start_websocket_operation` + +```python +async def start_websocket_operation( + self, + websocket: WebSocket, + data: Any, + context_value: Any, + query_document: DocumentNode, + operation_id: str, + operations: Dict[str, Operation], +) -> None: + ... +``` + + +#### `stop_websocket_operation` + +```python +async def stop_websocket_operation( + self, + websocket: WebSocket, + operation: Operation, +) -> None: + ... +``` + +Stops specified GraphQL operation for given connection. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + +[`operation`](types-reference.md#operation): an [`Operation`](types-reference.md#operation) to stop. + + +#### `observe_async_results` + +```python +async def observe_async_results( + self, + websocket: WebSocket, + results: AsyncGenerator, + operation_id: str, +) -> None: + ... +``` + +Converts results from Ariadne's `subscribe` generator into websocket +messages it next sends to the client. + + +##### Required arguments + +`websocket`: the `WebSocket` instance from Starlette or FastAPI. + +`results`: the `AsyncGenerator` returned from Ariadne's +`subscribe` function. + +`operation_id`: a `str` with id of operation. + + +- - - - - + + +## `GraphQLWebsocketHandler` + +```python +class GraphQLWebsocketHandler(GraphQLHandler): + ... +``` + +Base class for ASGI websocket connection handlers. + + +### Constructor + +```python +def __init__( + self, + on_connect: Optional[OnConnect] = None, + on_disconnect: Optional[OnDisconnect] = None, + on_operation: Optional[OnOperation] = None, + on_complete: Optional[OnComplete] = None, +): + ... +``` + +Initialize websocket handler with optional options specific to it. + + +#### Optional arguments: + +`on_connect`: an [`OnConnect`](types-reference.md#onconnect) callback used on new websocket connection. + +`on_disconnect`: an [`OnDisconnect`](types-reference.md#ondisconnect) callback used when existing +websocket connection is closed. + +`on_operation`: an [`OnOperation`](types-reference.md#onoperation) callback, used when new GraphQL +operation is received from websocket connection. + +`on_complete`: an [`OnComplete`](types-reference.md#oncomplete) callback, used when GraphQL operation +received over the websocket connection was completed. + + +### Methods + +#### `handle_websocket` + +```python +async def handle_websocket(self, websocket: Any) -> None: + ... +``` + +Abstract method for handling the websocket connection. + + +#### `configure` + +```python +def configure( + self, + *args, + http_handler: Optional[GraphQLHttpHandlerBase], + **kwargs, +) -> None: + ... +``` + +Configures the handler with options from the ASGI application. + +Called by Ariadne ASGI GraphQL application as part of its +initialization, propagating the configuration to it's handlers. + + +##### Optional arguments + +`http_handler`: the `GraphQLHttpHandlerBase` subclass instance to use +to execute the `Query` and `Mutation` operations made over the +websocket connections. \ No newline at end of file diff --git a/website/versioned_docs/version-0.24/constants-reference.md b/website/versioned_docs/version-0.24/constants-reference.md new file mode 100644 index 00000000..69b7cd3e --- /dev/null +++ b/website/versioned_docs/version-0.24/constants-reference.md @@ -0,0 +1,55 @@ +--- +id: version-0.24-constants-reference +title: Constants reference +sidebar_label: ariadne.constants +original_id: constants-reference +--- + +Following constants are importable from `ariadne.constants` module: + + +## `CONTENT_TYPE_JSON` + +```python +CONTENT_TYPE_JSON = 'application/json; charset=UTF-8' +``` + + +- - - - - + + +## `CONTENT_TYPE_TEXT_HTML` + +```python +CONTENT_TYPE_TEXT_HTML = 'text/html; charset=UTF-8' +``` + + +- - - - - + + +## `CONTENT_TYPE_TEXT_PLAIN` + +```python +CONTENT_TYPE_TEXT_PLAIN = 'text/plain; charset=UTF-8' +``` + + +- - - - - + + +## `DATA_TYPE_JSON` + +```python +DATA_TYPE_JSON = 'application/json' +``` + + +- - - - - + + +## `DATA_TYPE_MULTIPART` + +```python +DATA_TYPE_MULTIPART = 'multipart/form-data' +``` \ No newline at end of file diff --git a/website/versioned_docs/version-0.24/exceptions-reference.md b/website/versioned_docs/version-0.24/exceptions-reference.md new file mode 100644 index 00000000..a22a8158 --- /dev/null +++ b/website/versioned_docs/version-0.24/exceptions-reference.md @@ -0,0 +1,158 @@ +--- +id: version-0.24-exceptions-reference +title: Exceptions reference +sidebar_label: ariadne.exceptions +original_id: exceptions-reference +--- + +Ariadne defines some custom exception types that can be imported from `ariadne.exceptions` module: + + + +## `GraphQLFileSyntaxError` + +```python +class GraphQLFileSyntaxError(Exception): + ... +``` + +Raised by `load_schema_from_path` when loaded GraphQL file has invalid syntax. + + +### Constructor + +```python +def __init__(self, file_path: Union[str, os.PathLike], message: str): + ... +``` + +Initializes the `GraphQLFileSyntaxError` with file name and error. + + +#### Required arguments + +`file_path`: a `str` or `PathLike` object pointing to a file that +failed to validate. + +`message`: a `str` with validation message. + + +### Methods + +#### `format_message` + +```python +def format_message( + self, + file_path: Union[str, os.PathLike], + message: str, +) -> None: + ... +``` + +Builds final error message from path to schema file and error message. + +Returns `str` with final error message. + + +##### Required arguments + +`file_path`: a `str` or `PathLike` object pointing to a file that +failed to validate. + +`message`: a `str` with validation message. + + +#### `__str__` + +```python +def __str__(self) -> None: + ... +``` + +Returns error message. + + +- - - - - + + +## `HttpBadRequestError` + +```python +class HttpBadRequestError(HttpError): + ... +``` + +Raised when request did not contain the data required to execute +the GraphQL query. + + +### Constructor + +```python +def __init__(self, message: Optional[str] = None): + ... +``` + +Initializes the `HttpBadRequestError` with optional error message. + + +- - - - - + + +## `HttpError` + +```python +class HttpError(Exception): + ... +``` + +Base class for HTTP errors raised inside the ASGI and WSGI servers. + + +### Constructor + +```python +def __init__(self, status: str, message: Optional[str] = None): + ... +``` + +Initializes the `HttpError` with a status and optional error message. + + +#### Arguments + +`status`: HTTP status code as `HttpStatusResponse`. +`message`: Optional error message to return in the response body. + + +- - - - - + + +## `HttpStatusResponse` + +```python +class HttpStatusResponse(Enum): + ... +``` + + +- - - - - + + +## `WebSocketConnectionError` + +```python +class WebSocketConnectionError(Exception): + ... +``` + +Special error class enabling custom error reporting for on_connect + + +### Constructor + +```python +def __init__(self, payload: Optional[Union[dict, str]] = None): + ... +``` \ No newline at end of file diff --git a/website/versioned_docs/version-0.24/explorers.md b/website/versioned_docs/version-0.24/explorers.md new file mode 100644 index 00000000..0ea482a8 --- /dev/null +++ b/website/versioned_docs/version-0.24/explorers.md @@ -0,0 +1,227 @@ +--- +id: version-0.24-explorers +title: GraphQL explorers +original_id: explorers +--- + +Explorers provide web-based GUI for interacting with your GraphQL API. Ariadne implements support for multiple explorers out of the box. It also supports disabling explorer UI altogether. + +Ariadne also makes it possible for developers to [implement custom support for any explorer](#custom-explorer). + + +## GraphiQL 2 + +```python +from ariadne.explorer import ExplorerGraphiQL +``` + +Default GraphQL explorer in Ariadne since 0.17 release. + + +### Supported options + +`ExplorerGraphiQL` constructor accepts following options: + +- `title: str = "Ariadne GraphQL"` - Used for page title and loading message. +- `default_query: str = "..."` - Default content of editor area. +- `explorer_plugin: bool = False` - Enables [GraphQL Explorer plugin](https://www.youtube.com/watch?v=8DmtCPX4tdo&nounroll=1). +- `subscription_url: str = ""` - URL to use by GraphQL subscription connections. Defaults to current URL, but with `http` protocol replaced with `ws`. + + +## Apollo Sandbox + +```python +from ariadne.explorer import ExplorerApollo +``` + +Embedded Apollo Sandbox. + + +### Supported options + +`ExplorerApollo` constructor accepts following options: + +- `title: str = "Ariadne GraphQL"` - Used for page title and loading message. +- `default_query: str = "..."` - Default content of editor area. +- `include_cookies: bool: bool = False` - Controls if Apollo explorer should include cookies in requests. + + +## GraphQL Playground + +```python +from ariadne.explorer import ExplorerPlayground +``` + +GraphQL Playground was default explorer in Ariadne until 0.17 release. **It's no longer maintained**. with its features being merged in to GraphiQL 2. It's provided by Ariadne as an alternative for teams and projects that don't want to make a switch yet. + + +### Supported options + +`ExplorerPlayground` constructor accepts following options: + +- `title: str = "Ariadne GraphQL"` - Used for page title and loading message. +- `share_enabled: bool = False` - Controls share playground feature. +- `editor_cursor_shape: Optional[str] = None` - Controls `editor.cursorShape` setting (defaults to `"line"`, can be changed to `"block"` or `"underline"`). +- `editor_font_family: Optional[str] = None` - Controls `editor.fontFamily` setting (defaults to `"'Source Code Pro', 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace"`). +- `editor_font_size: Optional[int] = None` - Controls `editor.fontSize` setting (defaults to `14`). +- `editor_reuse_headers: Optional[bool] = None` - Controls `editor.reuseHeaders` setting (`true` by default). +- `editor_theme: Optional[str] = None` - Controls `editor.theme` setting (`"dark"` by default, can be changed to `"light"`). +- `general_beta_updates: Optional[bool] = None` - Controls `general.betaUpdates` setting (`false` by default). +- `prettier_print_width: Optional[int] = None` - Controls `prettier.printWidth` setting (`80` by default). +- `prettier_tab_width: Optional[int] = None` - Controls `prettier.tabWidth` setting (`2` by default). +- `prettier_use_tabs: Optional[bool] = None` - Controls `prettier.useTabs` setting (`false` by default). +- `request_credentials: Optional[str] = None` - Controls `request.credentials` setting (`"omit"` by default, can be changed to `"include"` or `"same-origin"`). +- `request_global_headers: Optional[Dict[str, str]] = None` - Controls `request.globalHeaders` setting (`{}` by default). +- `schema_polling_enable: Optional[bool] = None` - Controls `schema.polling.enable` setting (`true` by default). +- `schema_polling_endpoint_filter: Optional[str] = None` - Controls `schema.polling.endpointFilter` setting (defaults to `"*localhost*"`). +- `schema_polling_interval: Optional[int] = None` - Controls `schema.polling.interval` setting (defaults to `2000`). +- `schema_disable_comments: Optional[bool] = None` - Controls `schema.disableComments` setting (defaults to `true`). +- `tracing_hide_tracing_response: Optional[bool] = None` - Controls `tracing.hideTracingResponse` setting (`false` by default). +- `tracing_tracing_supported: Optional[bool] = None` - Controls `tracing.tracingSupported` setting (`true` by default). +- `query_plan_hide_query_plan_response: Optional[bool] = None` - Controls `query.plan.hideQueryPlanResponse` setting (`false` by default). + +See Playground's readme for [complete reference of its settings](https://github.com/graphql/graphql-playground#settings). + + +## ExplorerHttp405 + +```python +from ariadne.explorer import ExplorerHttp405 +``` + +This explorer always triggers HTTP 405 "method not allowed" response from Ariadne, serving as a way to disable GraphQL explorer. + + +## Custom explorer + +You can implement custom GraphQL explorer for Ariadne. All explorers should extend `Explorer` base class importable from `ariadne.explorer`: + +```python +from ariadne.explorer import Explorer + + +class MyExplorer(Explorer): + ... +``` + +Explorer class needs to implement `html` method taking single argument, "request" instance specific to used HTTP protocol implementation. This method should return HTML code for explorer, or `None`: + +```python +from ariadne.explorer import Explorer + + +class MyExplorer(Explorer): + def html(self, request): + return "
Hello world
" +``` + +If `html` method returns `None`, HTTP 405 "method not allowed" error will be returned by the HTTP server: + +```python +from ariadne.explorer import Explorer + + +class MyExplorer(Explorer): + def html(self, request): + if not request.user.is_staff: + return None + + return "
Hello world
" +``` + +Some HTTP servers implemented by Ariadne (eg. `ariadne.asgi.GraphQL`) also support `html` returning awaitable: + +```python +from ariadne.explorer import Explorer +from myapp.auth import get_authorized_user + + +class MyExplorer(Explorer): + def html(self, request): + return self.html_future(request) + + async def html_future(self, request) + auth_token = request.headers.get("authorization") + if not request.headers.get("authorization"): + return None + + if not await get_authorized_user(request, auth_token): + return None + + return "
Hello world
" +``` + + +### `render_template` + +Ariadne implements minimal template engine for use by explorers. This engine is available through the `render_template` utility function importable from `ariadne.explorer`. It takes template string (inspired by Django) and returns final string with values and blocks evaluated: + +```python +import json +from ariadne.explorer import render_template + +template = "Hello {% if name %}{{ name }}{% else %}guest{% endif %}!" + +result = render_template(template, {"name": "Alic<3"}) +assert result == "Hello Alic<3!" + +result_2 = render_template(template) +assert result_2 == "Hello guest!" +``` + +`if` and `ifnot` blocks can be used for conditions. Those take one or more variable names (separated by space) and require all of them to be true for `if` and false for `ifnot`: + +```python +import json +from ariadne.explorer import render_template + +template = """ +Hello {% if name %}{{ name }}{% else %}guest{% endif %}! + +You are {% ifnot admin %}a member{% else %}an admin{% endif %}. + +Your level is {% if admin moderator %}staff{% else %}member{% endif %}. +""" + +result = render_template( + template, + { + "name": "Alic<3", + "admin" False, + "moderator", False, + }, +) + +assert result == """ +Hello Alic<3! + +You are a member. + +Your level is member. +""" +``` + +Values are escaped on render. Use `{% raw value_name %}` to render unfiltered values: + +```python +import json +from ariadne.explorer import render_template + +template = """ + +""" + +result = render_template( + template, + { + "safe_value": json.dumps({"a": "b"}), + }, +) + +assert result == """ + +""" \ No newline at end of file