Skip to content

Commit cf2d1ef

Browse files
author
m.kindritskiy
committed
return ExecutionResult from Schema.execute/execute_async
1 parent 059721f commit cf2d1ef

20 files changed

+193
-288
lines changed

docs/changelog/changes_07.rst

-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ Changes in 0.7
9090
- Added support for extensions :ref:`Check extensions documentation <extensions-doc>`
9191

9292
- Added ``QueryParseCache`` extension - cache parsed graphql queries ast.
93-
- Added ``QueryTransformCache`` extension - cache transformed graphql ast into query Node.
9493
- Added ``QueryValidationCache`` extension - cache query validation.
9594
- Added ``QueryDepthValidator`` extension - validate query depth
9695
- Added ``PrometheusMetrics`` extension - wrapper around ``GraphMetrics`` visitor

docs/changelog/changes_08.rst

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ Changes in 0.8
1616
- Drop `hiku.federation.endpoint` - use `hiku.endpoint` instead
1717
- Merge `tests.test_federation.test_endpoint` and `tests.test_federation.test_engine` into `tests.test_federation.test_schema`
1818
- Change `QueryDepthValidator` hook to `on_validate`
19+
- Change `GraphQLResponse` type - it now has both `data` and `errors` fields
20+
- Rename `on_dispatch` hook to `on_operation`
21+
- Remove old `on_operation` hook
1922

2023
Backward-incompatible changes
2124
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

docs/extensions.rst

+2-4
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,15 @@ graph processing.
1313
Here are all the methods that can be implemented:
1414

1515
- :meth:`~hiku.extensions.Extension.on_graph` - when endpoint is created and transformations applied to graph
16-
- :meth:`~hiku.extensions.Extension.on_dispatch` - when query is dispatched to the endpoint
17-
- :meth:`~hiku.extensions.Extension.on_parse` - when query string is parsed into ast
18-
- :meth:`~hiku.extensions.Extension.on_operation` - when query ast parsed into query Node
16+
- :meth:`~hiku.extensions.Extension.on_operation` - when query is executed by the schema
17+
- :meth:`~hiku.extensions.Extension.on_parse` - when query string is parsed into ast and the into query Node
1918
- :meth:`~hiku.extensions.Extension.on_validate` - when query is validated
2019
- :meth:`~hiku.extensions.Extension.on_execute` - when query is executed by engine
2120

2221
Built-in extensions
2322
~~~~~~~~~~~~~~~~~~~
2423

2524
- ``QueryParseCache`` - cache parsed graphql queries ast.
26-
- ``QueryTransformCache`` - cache transformed graphql ast into query Node.
2725
- ``QueryValidationCache`` - cache query validation.
2826
- ``QueryDepthValidator`` - validate query depth
2927
- ``PrometheusMetrics`` - wrapper around ``GraphMetrics`` visitor

hiku/endpoint/graphql.py

+60-28
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,37 @@
1-
import typing as t
1+
from typing import Any, Optional, List, Dict, Union, overload
2+
from typing_extensions import TypedDict
23

34
from abc import ABC
45
from asyncio import gather
56

67

7-
from hiku.schema import GraphQLError, GraphQLRequest, GraphQLResponse, Schema
8-
from ..graph import Graph
8+
from hiku.schema import ExecutionResult, GraphQLError, Schema
99

1010

11-
BatchedRequest = t.List[GraphQLRequest]
12-
BatchedResponse = t.List[GraphQLResponse]
11+
class GraphQLErrorObject(TypedDict):
12+
message: str
1313

14-
SingleOrBatchedRequest = t.Union[GraphQLRequest, BatchedRequest]
15-
SingleOrBatchedResponse = t.Union[GraphQLResponse, BatchedResponse]
1614

15+
class GraphQLRequest(TypedDict, total=False):
16+
query: str
17+
variables: Optional[Dict[Any, Any]]
18+
operationName: Optional[str]
1719

18-
# TODO: do we need it ?
19-
G = t.TypeVar("G", bound=Graph)
20-
C = t.TypeVar("C")
2120

21+
class GraphQLResponse(TypedDict, total=False):
22+
data: Optional[Dict[str, object]]
23+
errors: Optional[List[object]]
24+
extensions: Optional[Dict[str, object]]
2225

23-
class BaseGraphQLEndpoint(ABC, t.Generic[C]):
26+
27+
BatchedRequest = List[GraphQLRequest]
28+
BatchedResponse = List[GraphQLResponse]
29+
30+
SingleOrBatchedRequest = Union[GraphQLRequest, BatchedRequest]
31+
SingleOrBatchedResponse = Union[GraphQLResponse, BatchedResponse]
32+
33+
34+
class BaseGraphQLEndpoint(ABC):
2435
"""TODO: add doc explaining the purpose of this class over plain schema"""
2536

2637
schema: Schema
@@ -33,10 +44,18 @@ def __init__(
3344
self.schema = schema
3445
self.batching = batching
3546

47+
def process_result(self, result: ExecutionResult) -> GraphQLResponse:
48+
data: GraphQLResponse = {"data": result.data}
49+
50+
if result.error:
51+
data["errors"] = [{"message": e} for e in result.error.errors]
52+
53+
return data
54+
3655

3756
class BaseSyncGraphQLEndpoint(BaseGraphQLEndpoint):
3857
def dispatch(
39-
self, data: GraphQLRequest, context: t.Optional[t.Dict] = None
58+
self, data: GraphQLRequest, context: Optional[Dict] = None
4059
) -> GraphQLResponse:
4160
"""
4261
Dispatch graphql request to graph
@@ -46,32 +65,45 @@ def dispatch(
4665
4766
Returns: graphql response: data or errors
4867
"""
49-
return self.schema.execute_sync(data, context)
68+
assert "query" in data, "query is required"
69+
result = self.schema.execute_sync(
70+
query=data["query"],
71+
variables=data.get("variables"),
72+
operation_name=data.get("operationName"),
73+
context=context,
74+
)
75+
return self.process_result(result)
5076

5177

5278
class BaseAsyncGraphQLEndpoint(BaseGraphQLEndpoint):
5379
async def dispatch(
54-
self, data: GraphQLRequest, context: t.Optional[t.Dict] = None
80+
self, data: GraphQLRequest, context: Optional[Dict] = None
5581
) -> GraphQLResponse:
56-
# TODO: what it we want to override exception handling?
57-
return await self.schema.execute(data, context)
82+
assert "query" in data, "query is required"
83+
result = await self.schema.execute(
84+
query=data["query"],
85+
variables=data.get("variables"),
86+
operation_name=data.get("operationName"),
87+
context=context,
88+
)
89+
return self.process_result(result)
5890

5991

6092
class GraphQLEndpoint(BaseSyncGraphQLEndpoint):
61-
@t.overload
93+
@overload
6294
def dispatch(
63-
self, data: GraphQLRequest, context: t.Optional[t.Dict] = None
95+
self, data: GraphQLRequest, context: Optional[Dict] = None
6496
) -> GraphQLResponse:
6597
...
6698

67-
@t.overload
99+
@overload
68100
def dispatch(
69-
self, data: BatchedRequest, context: t.Optional[t.Dict] = None
101+
self, data: BatchedRequest, context: Optional[Dict] = None
70102
) -> BatchedResponse:
71103
...
72104

73105
def dispatch(
74-
self, data: SingleOrBatchedRequest, context: t.Optional[t.Dict] = None
106+
self, data: SingleOrBatchedRequest, context: Optional[Dict] = None
75107
) -> SingleOrBatchedResponse:
76108
if isinstance(data, list):
77109
if not self.batching:
@@ -88,26 +120,26 @@ def dispatch(
88120
class BatchGraphQLEndpoint(GraphQLEndpoint):
89121
"""For backward compatibility"""
90122

91-
def __init__(self, *args: t.Any, **kwargs: t.Any):
123+
def __init__(self, *args: Any, **kwargs: Any):
92124
kwargs["batching"] = True
93125
super().__init__(*args, **kwargs)
94126

95127

96128
class AsyncGraphQLEndpoint(BaseAsyncGraphQLEndpoint):
97-
@t.overload
129+
@overload
98130
async def dispatch(
99-
self, data: GraphQLRequest, context: t.Optional[t.Dict] = None
131+
self, data: GraphQLRequest, context: Optional[Dict] = None
100132
) -> GraphQLResponse:
101133
...
102134

103-
@t.overload
135+
@overload
104136
async def dispatch(
105-
self, data: BatchedRequest, context: t.Optional[t.Dict] = None
137+
self, data: BatchedRequest, context: Optional[Dict] = None
106138
) -> BatchedResponse:
107139
...
108140

109141
async def dispatch(
110-
self, data: SingleOrBatchedRequest, context: t.Optional[t.Dict] = None
142+
self, data: SingleOrBatchedRequest, context: Optional[Dict] = None
111143
) -> SingleOrBatchedResponse:
112144
if isinstance(data, list):
113145
return list(
@@ -129,6 +161,6 @@ async def dispatch(
129161
class AsyncBatchGraphQLEndpoint(AsyncGraphQLEndpoint):
130162
"""For backward compatibility"""
131163

132-
def __init__(self, *args: t.Any, **kwargs: t.Any):
164+
def __init__(self, *args: Any, **kwargs: Any):
133165
kwargs["batching"] = True
134166
super().__init__(*args, **kwargs)

hiku/extensions/base_extension.py

+14-31
Original file line numberDiff line numberDiff line change
@@ -46,35 +46,21 @@ def on_graph(
4646
"""
4747
yield None
4848

49-
def on_dispatch(
49+
def on_operation(
5050
self, execution_context: ExecutionContext
5151
) -> AsyncIteratorOrIterator[None]:
52-
"""Called before and after the dispatch step.
52+
"""Called before and after the operation step.
5353
54-
Dispatch step is a step where the query is dispatched by to the endpoint
55-
to be parsed, validated and executed.
54+
Operation step is a step where the query is executed by schema.
5655
5756
At this step the:
58-
- execution_context.query_src is set to the query string
59-
from request
57+
- execution_context.query_src (if type str) is set to the query string
58+
- execution_context.query (if type Noe) is set to the query Node
6059
- execution_context.variables is set to the query variables
61-
from request
6260
- execution_context.operation_name is set to the query operation name
63-
from request
6461
- execution_context.query_graph is set to the query graph
6562
- execution_context.mutation_graph is set to the mutation graph
66-
- execution_context.context is set to the context from dispatch argument
67-
"""
68-
yield None
69-
70-
def on_operation(
71-
self, execution_context: ExecutionContext
72-
) -> AsyncIteratorOrIterator[None]:
73-
"""Called before and after the operation step.
74-
75-
Operation step is a step where the graphql ast is transformed into
76-
hiku's query ast and Operation type is created and assigned to the
77-
execution_context.operation.
63+
- execution_context.context is set to the context from execute argument
7864
"""
7965
yield None
8066

@@ -83,8 +69,12 @@ def on_parse(
8369
) -> AsyncIteratorOrIterator[None]:
8470
"""Called before and after the parsing step.
8571
86-
Parse step is when query string is parsed into graphql ast
87-
and will be assigned to the execution_context.graphql_document.
72+
Parse step is when query string is:
73+
- parsed into graphql ast and will be assigned to the
74+
execution_context.graphql_document
75+
- graphql ast is transformed into
76+
hiku's query ast and Operation type is created and assigned to the
77+
execution_context.operation.
8878
"""
8979
yield None
9080

@@ -142,9 +132,9 @@ def graph(self) -> "ExtensionContextManagerBase":
142132
Extension.on_graph.__name__, self.extensions, self.execution_context
143133
)
144134

145-
def dispatch(self) -> "ExtensionContextManagerBase":
135+
def operation(self) -> "ExtensionContextManagerBase":
146136
return ExtensionContextManagerBase(
147-
Extension.on_dispatch.__name__,
137+
Extension.on_operation.__name__,
148138
self.extensions,
149139
self.execution_context,
150140
)
@@ -154,13 +144,6 @@ def parsing(self) -> "ExtensionContextManagerBase":
154144
Extension.on_parse.__name__, self.extensions, self.execution_context
155145
)
156146

157-
def operation(self) -> "ExtensionContextManagerBase":
158-
return ExtensionContextManagerBase(
159-
Extension.on_operation.__name__,
160-
self.extensions,
161-
self.execution_context,
162-
)
163-
164147
def validation(self) -> "ExtensionContextManagerBase":
165148
return ExtensionContextManagerBase(
166149
Extension.on_validate.__name__,

hiku/extensions/query_transform_cache.py

-32
This file was deleted.

hiku/extensions/sentry.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def get_resource_name(self, execution_context: ExecutionContext) -> str:
2121
def _hash_query(self, query: str) -> str:
2222
return hashlib.md5(query.encode("utf-8")).hexdigest()
2323

24-
def on_dispatch(
24+
def on_operation(
2525
self, execution_context: ExecutionContext
2626
) -> Iterator[None]:
2727
self._operation_name = execution_context.operation_name

0 commit comments

Comments
 (0)