Skip to content

Commit 6959cb0

Browse files
committed
Allow passing DocumentNode to get_response to bypass validation
to add support for persisted queries, we want to bypass repeated validation of queries that have already been parsed and validated. This allows that by letting the user pass in the `DocumentNode` directly which bypasses much of the parsing and validation, making the query processing faster.
1 parent 637b36f commit 6959cb0

File tree

1 file changed

+62
-42
lines changed

1 file changed

+62
-42
lines changed

graphql_server/__init__.py

+62-42
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
from graphql.error import GraphQLError
2727
from graphql.execution import ExecutionResult, execute
28-
from graphql.language import OperationType, parse
28+
from graphql.language import OperationType, parse, DocumentNode
2929
from graphql.pyutils import AwaitableOrValue
3030
from graphql.type import GraphQLSchema, validate_schema
3131
from graphql.utilities import get_operation_ast
@@ -61,7 +61,7 @@
6161

6262
@dataclass
6363
class GraphQLParams:
64-
query: str
64+
query: str | DocumentNode
6565
variables: Optional[Dict[str, Any]] = None
6666
operation_name: Optional[str] = None
6767

@@ -158,16 +158,13 @@ def run_http_query(
158158
all_params: List[GraphQLParams] = [
159159
get_graphql_params(entry, extra_data) for entry in data
160160
]
161-
# print("GET ROOT VALUE 0", type(request_method), all_params)
162-
# print(dict(schema=schema, all_params=all_params, catch_exc=catch_exc, allow_only=allow_only_query, run_sync=run_sync))
163161

164162
results: List[Optional[AwaitableOrValue[ExecutionResult]]] = [
165163
get_response(
166164
schema, params, catch_exc, allow_only_query, run_sync, **execute_options
167165
)
168166
for params in all_params
169167
]
170-
# print("GET ROOT VALUE 1")
171168

172169
return GraphQLResponse(results=results, params=all_params)
173170

@@ -314,6 +311,55 @@ def assume_not_awaitable(_value: Any) -> bool:
314311
return False
315312

316313

314+
def parse_document(
315+
schema: GraphQLSchema,
316+
params: GraphQLParams,
317+
allow_only_query: bool = False,
318+
validation_rules: Optional[Collection[Type[ASTValidationRule]]] = None,
319+
max_errors: Optional[int] = None,
320+
) -> Optional[Dict]:
321+
if not params.query:
322+
raise HttpQueryError(400, "Must provide query string.")
323+
324+
if not isinstance(params.query, str) and not isinstance(params.query, DocumentNode):
325+
raise HttpQueryError(400, "Unexpected query type.")
326+
327+
if isinstance(params.query, DocumentNode):
328+
return params.query
329+
schema_validation_errors = validate_schema(schema)
330+
if schema_validation_errors:
331+
return ExecutionResult(data=None, errors=schema_validation_errors)
332+
333+
try:
334+
document = parse(params.query)
335+
except GraphQLError as e:
336+
return ExecutionResult(data=None, errors=[e])
337+
except Exception as e:
338+
e = GraphQLError(str(e), original_error=e)
339+
return ExecutionResult(data=None, errors=[e])
340+
341+
if allow_only_query:
342+
operation_ast = get_operation_ast(document, params.operation_name)
343+
if operation_ast:
344+
operation = operation_ast.operation.value
345+
if operation != OperationType.QUERY.value:
346+
raise HttpQueryError(
347+
405,
348+
f"Can only perform a {operation} operation" " from a POST request.",
349+
headers={"Allow": "POST"},
350+
)
351+
352+
validation_errors = validate(
353+
schema,
354+
document,
355+
rules=validation_rules,
356+
max_errors=max_errors,
357+
)
358+
if validation_errors:
359+
return ExecutionResult(data=None, errors=validation_errors)
360+
return document
361+
362+
317363
def get_response(
318364
schema: GraphQLSchema,
319365
params: GraphQLParams,
@@ -334,44 +380,18 @@ def get_response(
334380
belong to an exception class specified by catch_exc.
335381
"""
336382
# noinspection PyBroadException
383+
document = parse_document(
384+
schema,
385+
params,
386+
allow_only_query,
387+
validation_rules,
388+
max_errors,
389+
)
390+
if isinstance(document, ExecutionResult):
391+
return document
392+
if not isinstance(document, DocumentNode):
393+
raise Exception("GraphQL query could not be parsed properly.")
337394
try:
338-
if not params.query:
339-
raise HttpQueryError(400, "Must provide query string.")
340-
341-
# Sanity check query
342-
if not isinstance(params.query, str):
343-
raise HttpQueryError(400, "Unexpected query type.")
344-
345-
schema_validation_errors = validate_schema(schema)
346-
if schema_validation_errors:
347-
return ExecutionResult(data=None, errors=schema_validation_errors)
348-
349-
try:
350-
document = parse(params.query)
351-
except GraphQLError as e:
352-
return ExecutionResult(data=None, errors=[e])
353-
except Exception as e:
354-
e = GraphQLError(str(e), original_error=e)
355-
return ExecutionResult(data=None, errors=[e])
356-
357-
if allow_only_query:
358-
operation_ast = get_operation_ast(document, params.operation_name)
359-
if operation_ast:
360-
operation = operation_ast.operation.value
361-
if operation != OperationType.QUERY.value:
362-
raise HttpQueryError(
363-
405,
364-
f"Can only perform a {operation} operation"
365-
" from a POST request.",
366-
headers={"Allow": "POST"},
367-
)
368-
369-
validation_errors = validate(
370-
schema, document, rules=validation_rules, max_errors=max_errors
371-
)
372-
if validation_errors:
373-
return ExecutionResult(data=None, errors=validation_errors)
374-
375395
execution_result = execute(
376396
schema,
377397
document,

0 commit comments

Comments
 (0)