-
-
Notifications
You must be signed in to change notification settings - Fork 533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat/rustberry integration v2 #3504
base: main
Are you sure you want to change the base?
Changes from 20 commits
f8990c8
16a2bb4
d8f3d15
ca8129f
e7437e5
434653c
7fd22d0
d19a212
5b49773
2558c2d
845aa38
aa2bb83
377ac47
d928e57
17097a3
20340ed
5f85b24
76aa2bd
a5beaf1
384d281
59353ab
4b6d378
3ca28b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,9 +17,8 @@ | |
cast, | ||
) | ||
|
||
from graphql import GraphQLError, parse | ||
from graphql import GraphQLError, parse, validate | ||
from graphql import execute as original_execute | ||
from graphql.validation import validate | ||
|
||
from strawberry.exceptions import MissingQueryError | ||
from strawberry.extensions.runner import SchemaExtensionsRunner | ||
|
@@ -38,6 +37,7 @@ | |
|
||
from strawberry.extensions import SchemaExtension | ||
from strawberry.types import ExecutionContext | ||
from strawberry.types.execution import Executor, ParseOptions | ||
from strawberry.types.graphql import OperationType | ||
|
||
|
||
|
@@ -82,6 +82,7 @@ async def execute( | |
execution_context: ExecutionContext, | ||
execution_context_class: Optional[Type[GraphQLExecutionContext]] = None, | ||
process_errors: Callable[[List[GraphQLError], Optional[ExecutionContext]], None], | ||
executor: Executor, | ||
) -> ExecutionResult: | ||
extensions_runner = SchemaExtensionsRunner( | ||
execution_context=execution_context, | ||
|
@@ -95,30 +96,28 @@ async def execute( | |
if not execution_context.query: | ||
raise MissingQueryError() | ||
|
||
async with extensions_runner.parsing(): | ||
try: | ||
if not execution_context.graphql_document: | ||
execution_context.graphql_document = parse_document( | ||
execution_context.query, **execution_context.parse_options | ||
) | ||
async with extensions_runner.parsing(): | ||
try: | ||
if not execution_context.graphql_document: | ||
executor.parse(execution_context) | ||
|
||
except GraphQLError as exc: | ||
execution_context.errors = [exc] | ||
process_errors([exc], execution_context) | ||
return ExecutionResult( | ||
data=None, | ||
errors=[exc], | ||
extensions=await extensions_runner.get_extensions_results(), | ||
) | ||
except GraphQLError as exc: | ||
execution_context.errors = [exc] | ||
process_errors([exc], execution_context) | ||
return ExecutionResult( | ||
data=None, | ||
errors=[exc], | ||
extensions=await extensions_runner.get_extensions_results(), | ||
) | ||
|
||
if execution_context.operation_type not in allowed_operation_types: | ||
raise InvalidOperationTypeError(execution_context.operation_type) | ||
|
||
async with extensions_runner.validation(): | ||
_run_validation(execution_context) | ||
if execution_context.errors: | ||
process_errors(execution_context.errors, execution_context) | ||
return ExecutionResult(data=None, errors=execution_context.errors) | ||
async with extensions_runner.validation(): | ||
executor.validate(execution_context) | ||
if execution_context.errors: | ||
process_errors(execution_context.errors, execution_context) | ||
return ExecutionResult(data=None, errors=execution_context.errors) | ||
|
||
async with extensions_runner.executing(): | ||
if not execution_context.result: | ||
|
@@ -180,6 +179,7 @@ def execute_sync( | |
execution_context: ExecutionContext, | ||
execution_context_class: Optional[Type[GraphQLExecutionContext]] = None, | ||
process_errors: Callable[[List[GraphQLError], Optional[ExecutionContext]], None], | ||
executor: Executor, | ||
) -> ExecutionResult: | ||
extensions_runner = SchemaExtensionsRunner( | ||
execution_context=execution_context, | ||
|
@@ -193,30 +193,28 @@ def execute_sync( | |
if not execution_context.query: | ||
raise MissingQueryError() | ||
|
||
with extensions_runner.parsing(): | ||
try: | ||
if not execution_context.graphql_document: | ||
execution_context.graphql_document = parse_document( | ||
execution_context.query, **execution_context.parse_options | ||
) | ||
with extensions_runner.parsing(): | ||
try: | ||
if not execution_context.graphql_document: | ||
executor.parse(execution_context) | ||
|
||
except GraphQLError as exc: | ||
execution_context.errors = [exc] | ||
process_errors([exc], execution_context) | ||
return ExecutionResult( | ||
data=None, | ||
errors=[exc], | ||
extensions=extensions_runner.get_extensions_results_sync(), | ||
) | ||
except GraphQLError as exc: | ||
execution_context.errors = [exc] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (code-quality): Extract duplicate code into function ( |
||
process_errors([exc], execution_context) | ||
return ExecutionResult( | ||
data=None, | ||
errors=[exc], | ||
extensions=extensions_runner.get_extensions_results_sync(), | ||
) | ||
|
||
if execution_context.operation_type not in allowed_operation_types: | ||
raise InvalidOperationTypeError(execution_context.operation_type) | ||
|
||
with extensions_runner.validation(): | ||
_run_validation(execution_context) | ||
if execution_context.errors: | ||
process_errors(execution_context.errors, execution_context) | ||
return ExecutionResult(data=None, errors=execution_context.errors) | ||
with extensions_runner.validation(): | ||
executor.validate(execution_context) | ||
if execution_context.errors: | ||
process_errors(execution_context.errors, execution_context) | ||
return ExecutionResult(data=None, errors=execution_context.errors) | ||
|
||
with extensions_runner.executing(): | ||
if not execution_context.result: | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,35 @@ | ||||||||||||||||||||||
from typing import TYPE_CHECKING | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Consider adding a module-level docstring. Adding a module-level docstring to
Suggested change
|
||||||||||||||||||||||
|
||||||||||||||||||||||
from graphql import GraphQLError | ||||||||||||||||||||||
from rustberry import QueryCompiler | ||||||||||||||||||||||
|
||||||||||||||||||||||
from strawberry import Schema | ||||||||||||||||||||||
from strawberry.types.execution import ExecutionContext, Executor | ||||||||||||||||||||||
|
||||||||||||||||||||||
if TYPE_CHECKING: | ||||||||||||||||||||||
from rustberry._rustberry import Document | ||||||||||||||||||||||
|
||||||||||||||||||||||
RUSTBERRY_DOCUMENT_FIELD = "__rustberry_document" | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
||||||||||||||||||||||
class RustberryExecutor(Executor): | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Consider adding a docstring to the RustberryExecutor class. Adding a docstring to the
Suggested change
|
||||||||||||||||||||||
def __init__(self, schema: Schema) -> None: | ||||||||||||||||||||||
super().__init__(schema) | ||||||||||||||||||||||
self.compiler = QueryCompiler(schema.as_str()) | ||||||||||||||||||||||
|
||||||||||||||||||||||
def parse(self, execution_context: ExecutionContext) -> None: | ||||||||||||||||||||||
document = self.compiler.parse(execution_context.query) | ||||||||||||||||||||||
setattr(execution_context, RUSTBERRY_DOCUMENT_FIELD, document) | ||||||||||||||||||||||
execution_context.graphql_document = self.compiler.gql_core_ast_mirror(document) | ||||||||||||||||||||||
|
||||||||||||||||||||||
def validate( | ||||||||||||||||||||||
self, | ||||||||||||||||||||||
execution_context: ExecutionContext, | ||||||||||||||||||||||
) -> None: | ||||||||||||||||||||||
assert execution_context.graphql_document | ||||||||||||||||||||||
document: Document = getattr(execution_context, RUSTBERRY_DOCUMENT_FIELD, None) | ||||||||||||||||||||||
assert document, "Document not set - Required for Rustberry use" | ||||||||||||||||||||||
validation_successful = self.compiler.validate(document) | ||||||||||||||||||||||
if not validation_successful: | ||||||||||||||||||||||
execution_context.errors = execution_context.errors or [] | ||||||||||||||||||||||
execution_context.errors.append(GraphQLError("Validation failed")) | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Consider providing more context in the validation error message. Providing more context in the validation error message, such as which part of the document failed validation, would help in debugging and understanding the issue.
Suggested change
|
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -40,6 +40,7 @@ | |||||||||||||
from strawberry.types.types import StrawberryObjectDefinition | ||||||||||||||
|
||||||||||||||
from ..printer import print_schema | ||||||||||||||
from ..types.execution import Executor, GraphQlCoreExecutor | ||||||||||||||
from . import compat | ||||||||||||||
from .base import BaseSchema | ||||||||||||||
from .config import StrawberryConfig | ||||||||||||||
|
@@ -82,6 +83,7 @@ def __init__( | |||||||||||||
Dict[object, Union[Type, ScalarWrapper, ScalarDefinition]] | ||||||||||||||
] = None, | ||||||||||||||
schema_directives: Iterable[object] = (), | ||||||||||||||
executor_class: Optional[Type[Executor]] = None, | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Consider adding a docstring to the executor_class parameter. Adding a docstring to the
Suggested change
|
||||||||||||||
) -> None: | ||||||||||||||
self.query = query | ||||||||||||||
self.mutation = mutation | ||||||||||||||
|
@@ -176,6 +178,11 @@ def __init__( | |||||||||||||
formatted_errors = "\n\n".join(f"❌ {error.message}" for error in errors) | ||||||||||||||
raise ValueError(f"Invalid Schema. Errors:\n\n{formatted_errors}") | ||||||||||||||
|
||||||||||||||
if executor_class: | ||||||||||||||
self.executor = executor_class(self) | ||||||||||||||
else: | ||||||||||||||
self.executor = GraphQlCoreExecutor(self) | ||||||||||||||
|
||||||||||||||
def get_extensions( | ||||||||||||||
self, sync: bool = False | ||||||||||||||
) -> List[Union[Type[SchemaExtension], SchemaExtension]]: | ||||||||||||||
|
@@ -267,6 +274,7 @@ async def execute( | |||||||||||||
execution_context=execution_context, | ||||||||||||||
allowed_operation_types=allowed_operation_types, | ||||||||||||||
process_errors=self.process_errors, | ||||||||||||||
executor=self.executor, | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
return result | ||||||||||||||
|
@@ -299,6 +307,7 @@ def execute_sync( | |||||||||||||
execution_context=execution_context, | ||||||||||||||
allowed_operation_types=allowed_operation_types, | ||||||||||||||
process_errors=self.process_errors, | ||||||||||||||
executor=self.executor, | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
return result | ||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,5 +1,6 @@ | ||||||||||||||||||
from __future__ import annotations | ||||||||||||||||||
|
||||||||||||||||||
import abc | ||||||||||||||||||
import dataclasses | ||||||||||||||||||
from typing import ( | ||||||||||||||||||
TYPE_CHECKING, | ||||||||||||||||||
|
@@ -12,7 +13,7 @@ | |||||||||||||||||
) | ||||||||||||||||||
from typing_extensions import TypedDict | ||||||||||||||||||
|
||||||||||||||||||
from graphql import specified_rules | ||||||||||||||||||
from graphql import parse, specified_rules, validate | ||||||||||||||||||
|
||||||||||||||||||
from strawberry.utils.operation import get_first_operation, get_operation_type | ||||||||||||||||||
|
||||||||||||||||||
|
@@ -29,6 +30,45 @@ | |||||||||||||||||
from .graphql import OperationType | ||||||||||||||||||
|
||||||||||||||||||
|
||||||||||||||||||
class Executor(abc.ABC): | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Consider adding a docstring to the Executor class. Adding a docstring to the
Suggested change
|
||||||||||||||||||
def __init__(self, schema: Schema) -> None: | ||||||||||||||||||
self.schema = schema | ||||||||||||||||||
|
||||||||||||||||||
@abc.abstractmethod | ||||||||||||||||||
def parse(self, execution_context: ExecutionContext) -> None: ... | ||||||||||||||||||
|
||||||||||||||||||
@abc.abstractmethod | ||||||||||||||||||
def validate( | ||||||||||||||||||
self, | ||||||||||||||||||
execution_context: ExecutionContext, | ||||||||||||||||||
) -> None: ... | ||||||||||||||||||
|
||||||||||||||||||
|
||||||||||||||||||
class GraphQlCoreExecutor(Executor): | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Consider adding a docstring to the GraphQlCoreExecutor class. Adding a docstring to the
Suggested change
|
||||||||||||||||||
def __init__(self, schema: Schema) -> None: | ||||||||||||||||||
super().__init__(schema) | ||||||||||||||||||
|
||||||||||||||||||
def parse(self, execution_context: ExecutionContext) -> None: | ||||||||||||||||||
execution_context.graphql_document = parse( | ||||||||||||||||||
execution_context.query, **execution_context.parse_options | ||||||||||||||||||
) | ||||||||||||||||||
|
||||||||||||||||||
def validate( | ||||||||||||||||||
self, | ||||||||||||||||||
execution_context: ExecutionContext, | ||||||||||||||||||
) -> None: | ||||||||||||||||||
if ( | ||||||||||||||||||
len(execution_context.validation_rules) > 0 | ||||||||||||||||||
and execution_context.errors is None | ||||||||||||||||||
): | ||||||||||||||||||
assert execution_context.graphql_document | ||||||||||||||||||
execution_context.errors = validate( | ||||||||||||||||||
execution_context.schema._schema, | ||||||||||||||||||
execution_context.graphql_document, | ||||||||||||||||||
execution_context.validation_rules, | ||||||||||||||||||
) | ||||||||||||||||||
|
||||||||||||||||||
|
||||||||||||||||||
@dataclasses.dataclass | ||||||||||||||||||
class ExecutionContext: | ||||||||||||||||||
query: Optional[str] | ||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Consider making the executor parameter optional.
If the executor parameter is not provided, you could default to a standard executor like
GraphQlCoreExecutor
. This would make the function more flexible and backward-compatible.