Skip to content

Commit 27de7d3

Browse files
committed
Validation: add support of SDL to KnownTypeNamesRule
Replicates graphql/graphql-js@3b9ea61
1 parent 9c21478 commit 27de7d3

File tree

8 files changed

+252
-223
lines changed

8 files changed

+252
-223
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ a query language for APIs created by Facebook.
1313
[![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
1414

1515
The current version 1.0.1 of GraphQL-core-next is up-to-date with GraphQL.js version
16-
14.0.2. All parts of the API are covered by an extensive test suite of currently 1683
16+
14.0.2. All parts of the API are covered by an extensive test suite of currently 1679
1717
unit tests.
1818

1919

graphql/utilities/build_ast_schema.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,7 @@ def build_ast_schema(
106106
append_directive_def(def_)
107107

108108
if schema_def:
109-
operation_types: Dict[OperationType, Any] = get_operation_types(
110-
schema_def, node_map
111-
)
109+
operation_types: Dict[OperationType, Any] = get_operation_types(schema_def)
112110
else:
113111
operation_types = {
114112
OperationType.QUERY: node_map.get("Query"),
@@ -162,18 +160,11 @@ def resolve_type(type_ref: NamedTypeNode):
162160

163161

164162
def get_operation_types(
165-
schema: SchemaDefinitionNode, node_map: TypeDefinitionsMap
163+
schema: SchemaDefinitionNode
166164
) -> Dict[OperationType, NamedTypeNode]:
167165
op_types: Dict[OperationType, NamedTypeNode] = {}
168166
for operation_type in schema.operation_types:
169-
type_name = operation_type.type.name.value
170-
operation = operation_type.operation
171-
if type_name not in node_map:
172-
raise TypeError(
173-
f"Specified {operation.value} type '{type_name}'"
174-
" not found in document."
175-
)
176-
op_types[operation] = operation_type.type
167+
op_types[operation_type.operation] = operation_type.type
177168
return op_types
178169

179170

graphql/utilities/extend_schema.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -469,14 +469,9 @@ def extend_type(type_def: GraphQLType) -> GraphQLType:
469469
def resolve_type(type_ref: NamedTypeNode) -> GraphQLNamedType:
470470
type_name = type_ref.name.value
471471
existing_type = schema.get_type(type_name)
472-
if existing_type:
473-
return extend_named_type(existing_type)
474-
raise GraphQLError(
475-
f"Unknown type: '{type_name}'."
476-
" Ensure that this type exists either in the original schema,"
477-
" or is added in a type definition.",
478-
[type_ref],
479-
)
472+
if not existing_type:
473+
raise TypeError(f"Unknown type: '{type_name}'.")
474+
return extend_named_type(existing_type)
480475

481476
ast_builder = ASTDefinitionBuilder(
482477
type_definition_map, assume_valid=assume_valid, resolve_type=resolve_type
Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,82 @@
1-
from typing import List
1+
from typing import List, Sequence, Union, cast
22

33
from ...error import GraphQLError
4-
from ...language import NamedTypeNode
5-
from ...pyutils import suggestion_list
6-
from . import ValidationRule
4+
from ...language import (
5+
is_type_definition_node,
6+
is_type_system_definition_node,
7+
is_type_system_extension_node,
8+
Node,
9+
NamedTypeNode,
10+
TypeDefinitionNode,
11+
)
12+
from ...type import specified_scalar_types
13+
from ...pyutils import quoted_or_list, suggestion_list
14+
from . import ASTValidationRule, ValidationContext, SDLValidationContext
715

816
__all__ = ["KnownTypeNamesRule", "unknown_type_message"]
917

1018

1119
def unknown_type_message(type_name: str, suggested_types: List[str]) -> str:
1220
message = f"Unknown type '{type_name}'."
1321
if suggested_types:
14-
message += " Perhaps you meant {quoted_or_list(suggested_types)}?"
22+
message += f" Perhaps you meant {quoted_or_list(suggested_types)}?"
1523
return message
1624

1725

18-
class KnownTypeNamesRule(ValidationRule):
26+
class KnownTypeNamesRule(ASTValidationRule):
1927
"""Known type names
2028
2129
A GraphQL document is only valid if referenced types (specifically variable
2230
definitions and fragment conditions) are defined by the type schema.
2331
"""
2432

25-
def enter_object_type_definition(self, *_args):
26-
return self.SKIP
33+
def __init__(self, context: Union[ValidationContext, SDLValidationContext]) -> None:
34+
super().__init__(context)
35+
schema = context.schema
36+
self.existing_types_map = schema.type_map if schema else {}
2737

28-
def enter_interface_type_definition(self, *_args):
29-
return self.SKIP
38+
defined_types = []
39+
for def_ in context.document.definitions:
40+
if is_type_definition_node(def_):
41+
def_ = cast(TypeDefinitionNode, def_)
42+
defined_types.append(def_.name.value)
43+
self.defined_types = set(defined_types)
3044

31-
def enter_union_type_definition(self, *_args):
32-
return self.SKIP
45+
self.type_names = list(self.existing_types_map) + defined_types
3346

34-
def enter_input_object_type_definition(self, *_args):
35-
return self.SKIP
36-
37-
def enter_named_type(self, node: NamedTypeNode, *_args):
38-
schema = self.context.schema
47+
def enter_named_type(
48+
self, node: NamedTypeNode, _key, parent: Node, _path, ancestors: List[Node]
49+
):
3950
type_name = node.name.value
40-
if not schema.get_type(type_name):
51+
if (
52+
type_name not in self.existing_types_map
53+
and type_name not in self.defined_types
54+
):
55+
try:
56+
definition_node = ancestors[2]
57+
except IndexError:
58+
definition_node = parent
59+
is_sdl = is_sdl_node(definition_node)
60+
if is_sdl and type_name in specified_scalar_types:
61+
return
62+
63+
suggested_types = suggestion_list(
64+
type_name,
65+
list(specified_scalar_types) + self.type_names
66+
if is_sdl
67+
else self.type_names,
68+
)
4169
self.report_error(
42-
GraphQLError(
43-
unknown_type_message(
44-
type_name, suggestion_list(type_name, list(schema.type_map))
45-
),
46-
[node],
47-
)
70+
GraphQLError(unknown_type_message(type_name, suggested_types), node)
4871
)
72+
73+
74+
def is_sdl_node(value: Union[Node, Sequence[Node], None]) -> bool:
75+
return (
76+
value is not None
77+
and not isinstance(value, list)
78+
and (
79+
is_type_system_definition_node(cast(Node, value))
80+
or is_type_system_extension_node(cast(Node, value))
81+
)
82+
)

graphql/validation/specified_rules.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
UniqueOperationTypesRule,
131131
UniqueTypeNamesRule,
132132
UniqueDirectiveNamesRule,
133+
KnownTypeNamesRule,
133134
KnownDirectivesRule,
134135
UniqueDirectivesPerLocationRule,
135136
KnownArgumentNamesOnDirectivesRule,

tests/utilities/test_build_ast_schema.py

Lines changed: 0 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -773,134 +773,3 @@ def allows_to_disable_sdl_validation():
773773
"""
774774
build_schema(sdl, assume_valid=True)
775775
build_schema(sdl, assume_valid_sdl=True)
776-
777-
778-
def describe_failures():
779-
def unknown_type_referenced():
780-
sdl = """
781-
schema {
782-
query: Hello
783-
}
784-
785-
type Hello {
786-
bar: Bar
787-
}
788-
"""
789-
with raises(TypeError) as exc_info:
790-
build_schema(sdl)
791-
msg = str(exc_info.value)
792-
assert "Type 'Bar' not found in document." in msg
793-
794-
def unknown_type_in_interface_list():
795-
sdl = """
796-
type Query implements Bar {
797-
field: String
798-
}
799-
"""
800-
with raises(TypeError) as exc_info:
801-
build_schema(sdl)
802-
msg = str(exc_info.value)
803-
assert "Type 'Bar' not found in document." in msg
804-
805-
def unknown_type_in_union_list():
806-
sdl = """
807-
union TestUnion = Bar
808-
type Query { testUnion: TestUnion }
809-
"""
810-
with raises(TypeError) as exc_info:
811-
build_schema(sdl)
812-
msg = str(exc_info.value)
813-
assert "Type 'Bar' not found in document." in msg
814-
815-
def unknown_query_type():
816-
sdl = """
817-
schema {
818-
query: Wat
819-
}
820-
821-
type Hello {
822-
str: String
823-
}
824-
"""
825-
with raises(TypeError) as exc_info:
826-
build_schema(sdl)
827-
msg = str(exc_info.value)
828-
assert msg == "Specified query type 'Wat' not found in document."
829-
830-
def unknown_mutation_type():
831-
sdl = """
832-
schema {
833-
query: Hello
834-
mutation: Wat
835-
}
836-
837-
type Hello {
838-
str: String
839-
}
840-
"""
841-
with raises(TypeError) as exc_info:
842-
build_schema(sdl)
843-
msg = str(exc_info.value)
844-
assert msg == "Specified mutation type 'Wat' not found in document."
845-
846-
def unknown_subscription_type():
847-
sdl = """
848-
schema {
849-
query: Hello
850-
mutation: Wat
851-
subscription: Awesome
852-
}
853-
854-
type Hello {
855-
str: String
856-
}
857-
858-
type Wat {
859-
str: String
860-
}
861-
"""
862-
with raises(TypeError) as exc_info:
863-
build_schema(sdl)
864-
msg = str(exc_info.value)
865-
assert msg == "Specified subscription type 'Awesome' not found in document."
866-
867-
def does_not_consider_directive_names():
868-
sdl = """
869-
schema {
870-
query: Foo
871-
}
872-
873-
directive @ Foo on QUERY
874-
"""
875-
with raises(TypeError) as exc_info:
876-
build_schema(sdl)
877-
msg = str(exc_info.value)
878-
assert msg == "Specified query type 'Foo' not found in document."
879-
880-
def does_not_consider_operation_names():
881-
sdl = """
882-
schema {
883-
query: Foo
884-
}
885-
886-
type Hello {
887-
str: String
888-
}
889-
"""
890-
with raises(TypeError) as exc_info:
891-
build_schema(sdl)
892-
msg = str(exc_info.value)
893-
assert msg == "Specified query type 'Foo' not found in document."
894-
895-
def does_not_consider_fragment_names():
896-
sdl = """
897-
schema {
898-
query: Foo
899-
}
900-
901-
fragment Foo on Type { field }
902-
"""
903-
with raises(TypeError) as exc_info:
904-
build_schema(sdl)
905-
msg = str(exc_info.value)
906-
assert msg == "Specified query type 'Foo' not found in document."

tests/utilities/test_extend_schema.py

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,39 +1069,6 @@ def does_not_allow_replacing_an_existing_enum_value():
10691069
" It cannot also be defined in this type extension."
10701070
)
10711071

1072-
def does_not_allow_referencing_an_unknown_type():
1073-
unknown_type_error = (
1074-
"Unknown type: 'Quix'. Ensure that this type exists either"
1075-
" in the original schema, or is added in a type definition."
1076-
)
1077-
1078-
type_sdl = """
1079-
extend type Bar {
1080-
quix: Quix
1081-
}
1082-
"""
1083-
with raises(GraphQLError) as exc_info:
1084-
extend_test_schema(type_sdl)
1085-
assert str(exc_info.value).startswith(unknown_type_error)
1086-
1087-
interface_sdl = """
1088-
extend interface SomeInterface {
1089-
quix: Quix
1090-
}
1091-
"""
1092-
with raises(GraphQLError) as exc_info:
1093-
extend_test_schema(interface_sdl)
1094-
assert str(exc_info.value).startswith(unknown_type_error)
1095-
1096-
input_sdl = """
1097-
extend input SomeInput {
1098-
quix: Quix
1099-
}
1100-
"""
1101-
with raises(GraphQLError) as exc_info:
1102-
extend_test_schema(input_sdl)
1103-
assert str(exc_info.value).startswith(unknown_type_error)
1104-
11051072
def does_not_allow_extending_an_unknown_type():
11061073
for sdl in [
11071074
"extend scalar UnknownType @foo",

0 commit comments

Comments
 (0)