Repository: mirumee/ariadne

Issue:
Pass in data to get_context_for_request
That way you can write something like:
```python
    async def get_context_for_request(
        self,
        request: Any,
        *, data: Dict[str, Any] = None
    ) -> Any:
        if callable(self.context_value):
            context = self.context_value(request)
            if isawaitable(context):
                context = await context
            return context

        data = data or {}

        query: Optional[str] = data.get("query")
        variables: Optional[Dict[str, Any]] = data.get("variables")
        operation_name: Optional[str] = data.get("operationName")

        return self.context_value or {
            "request": request,
            "variables": variables,
            "operation_name": operation_name,
            "query": query,
       }
```

that way the tracing extension can access `operation_name`, `variables`, and `query` which is very helpful metadata context.

Comments:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 57f4b2f..71e21a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
 - Changed `logger` option to also support `Logger` and `LoggerAdapter` instance in addition to `str` with logger name.
 - Added support for `@tag` directive used by Apollo Federation.
 - Moved project configuration from `setup.py` to `pyproject.toml`.
+- Changed `context_value` option in ASGI and WSGI applications for callables to take query data as second argument.
 - Changed `root_value` option in ASGI and WSGI applications for callables to take operation and and variables in addition to context and parsed query.
 - Added `execution_context_class` option to ASGI and WSGI applications.
 - Added `query_parser` option to ASGI and WSGI `GraphQL` applications that enables query parsing customization.
diff --git a/ariadne/asgi/handlers/base.py b/ariadne/asgi/handlers/base.py
index acb24c4..57a7bb3 100644
--- a/ariadne/asgi/handlers/base.py
+++ b/ariadne/asgi/handlers/base.py
@@ -70,9 +70,10 @@ class GraphQLHandler(ABC):
     async def get_context_for_request(
         self,
         request: Any,
+        data: dict,
     ) -> Any:
         if callable(self.context_value):
-            context = self.context_value(request)
+            context = self.context_value(request, data)  # type: ignore
             if isawaitable(context):
                 context = await context
             return context
diff --git a/ariadne/asgi/handlers/graphql_transport_ws.py b/ariadne/asgi/handlers/graphql_transport_ws.py
index 0ff3132..6148a8c 100644
--- a/ariadne/asgi/handlers/graphql_transport_ws.py
+++ b/ariadne/asgi/handlers/graphql_transport_ws.py
@@ -181,7 +181,7 @@ class GraphQLTransportWSHandler(GraphQLWebsocketHandler):
 
         validate_data(data)
 
-        context_value = await self.get_context_for_request(websocket)
+        context_value = await self.get_context_for_request(websocket, data)
 
         try:
             query_document = parse_query(context_value, self.query_parser, data)
diff --git a/ariadne/asgi/handlers/graphql_ws.py b/ariadne/asgi/handlers/graphql_ws.py
index 0d94ccc..67003e8 100644
--- a/ariadne/asgi/handlers/graphql_ws.py
+++ b/ariadne/asgi/handlers/graphql_ws.py
@@ -110,7 +110,7 @@ class GraphQLWSHandler(GraphQLWebsocketHandler):
         operations: Dict[str, Operation],
     ) -> None:
         validate_data(data)
-        context_value = await self.get_context_for_request(websocket)
+        context_value = await self.get_context_for_request(websocket, data)
 
         try:
             query_document = parse_query(context_value, self.query_parser, data)
diff --git a/ariadne/asgi/handlers/http.py b/ariadne/asgi/handlers/http.py
index 4c533bb..6763349 100644
--- a/ariadne/asgi/handlers/http.py
+++ b/ariadne/asgi/handlers/http.py
@@ -91,7 +91,7 @@ class GraphQLHTTPHandler(GraphQLHttpHandlerBase):
         query_document: Optional[DocumentNode] = None,
     ) -> GraphQLResult:
         if context_value is None:
-            context_value = await self.get_context_for_request(request)
+            context_value = await self.get_context_for_request(request, data)
 
         extensions = await self.get_extensions_for_request(request, context_value)
         middleware = await self.get_middleware_for_request(request, context_value)
diff --git a/ariadne/types.py b/ariadne/types.py
index 74046b9..eea6c0d 100644
--- a/ariadne/types.py
+++ b/ariadne/types.py
@@ -39,7 +39,11 @@ SubscriptionResult = Tuple[
 Subscriber = Callable[..., AsyncGenerator]
 ErrorFormatter = Callable[[GraphQLError, bool], dict]
 
-ContextValue = Union[Any, Callable[[Any], Any]]
+ContextValue = Union[
+    Any,
+    Callable[[Any], Any],  # TODO: remove in 0.19
+    Callable[[Any, dict], Any],
+]
 RootValue = Union[
     Any,
     Callable[[Optional[Any], DocumentNode], Any],  # TODO: remove in 0.19
diff --git a/ariadne/wsgi.py b/ariadne/wsgi.py
index 82bc7c4..74921cd 100644
--- a/ariadne/wsgi.py
+++ b/ariadne/wsgi.py
@@ -206,7 +206,7 @@ class GraphQL:
         return combine_multipart_data(operations, files_map, form.files)
 
     def execute_query(self, environ: dict, data: dict) -> GraphQLResult:
-        context_value = self.get_context_for_request(environ)
+        context_value = self.get_context_for_request(environ, data)
         extensions = self.get_extensions_for_request(environ, context_value)
         middleware = self.get_middleware_for_request(environ, context_value)
 
@@ -227,9 +227,11 @@ class GraphQL:
             execution_context_class=self.execution_context_class,
         )
 
-    def get_context_for_request(self, environ: dict) -> Optional[ContextValue]:
+    def get_context_for_request(
+        self, environ: dict, data: dict
+    ) -> Optional[ContextValue]:
         if callable(self.context_value):
-            return self.context_value(environ)
+            return self.context_value(environ, data)  # type: ignore
         return self.context_value or {"request": environ}
 
     def get_extensions_for_request(


Test patch:
diff --git a/tests/asgi/test_configuration.py b/tests/asgi/test_configuration.py
index f7c007d..81dbf5e 100644
--- a/tests/asgi/test_configuration.py
+++ b/tests/asgi/test_configuration.py
@@ -30,7 +30,7 @@ def test_custom_context_value_function_is_set_and_called_by_app(schema):
     app = GraphQL(schema, context_value=get_context_value)
     client = TestClient(app)
     client.post("/", json={"query": "{ status }"})
-    get_context_value.assert_called_once()
+    get_context_value.assert_called_once_with(ANY, {"query": "{ status }"})
 
 
 def test_custom_context_value_function_result_is_passed_to_resolvers(schema):
diff --git a/tests/wsgi/test_configuration.py b/tests/wsgi/test_configuration.py
index ed0a802..75ab0bd 100644
--- a/tests/wsgi/test_configuration.py
+++ b/tests/wsgi/test_configuration.py
@@ -47,7 +47,7 @@ def test_custom_context_value_function_is_called_with_request_value(schema):
     app = GraphQL(schema, context_value=get_context_value)
     request = {"CONTENT_TYPE": DATA_TYPE_JSON}
     app.execute_query(request, {"query": "{ status }"})
-    get_context_value.assert_called_once_with(request)
+    get_context_value.assert_called_once_with(request, {"query": "{ status }"})
 
 
 def test_custom_context_value_function_result_is_passed_to_resolvers(schema):