Skip to content

Commit ae0aff3

Browse files
committed
Fix ambiguity around when schema definition may be omitted
Replicates graphql/graphql-js@f201681
1 parent 891586d commit ae0aff3

File tree

6 files changed

+121
-35
lines changed

6 files changed

+121
-35
lines changed

src/graphql/utilities/print_schema.py

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -70,51 +70,59 @@ def print_filtered_schema(
7070

7171
def print_schema_definition(schema: GraphQLSchema) -> Optional[str]:
7272
"""Print GraphQL schema definitions."""
73-
if schema.description is None and is_schema_of_common_names(schema):
74-
return None
75-
76-
operation_types = []
77-
7873
query_type = schema.query_type
79-
if query_type:
80-
operation_types.append(f" query: {query_type.name}")
81-
8274
mutation_type = schema.mutation_type
83-
if mutation_type:
84-
operation_types.append(f" mutation: {mutation_type.name}")
85-
8675
subscription_type = schema.subscription_type
87-
if subscription_type:
88-
operation_types.append(f" subscription: {subscription_type.name}")
8976

90-
return print_description(schema) + "schema {\n" + "\n".join(operation_types) + "\n}"
77+
# Special case: When a schema has no root operation types, no valid schema
78+
# definition can be printed.
79+
if not query_type and not mutation_type and not subscription_type:
80+
return None
81+
82+
# Only print a schema definition if there is a description or if it should
83+
# not be omitted because of having default type names.
84+
if schema.description or not has_default_root_operation_types(schema):
85+
return (
86+
print_description(schema)
87+
+ "schema {\n"
88+
+ (f" query: {query_type.name}\n" if query_type else "")
89+
+ (f" mutation: {mutation_type.name}\n" if mutation_type else "")
90+
+ (
91+
f" subscription: {subscription_type.name}\n"
92+
if subscription_type
93+
else ""
94+
)
95+
+ "}"
96+
)
97+
98+
return None
9199

92100

93-
def is_schema_of_common_names(schema: GraphQLSchema) -> bool:
94-
"""Check whether this schema uses the common naming convention.
101+
def has_default_root_operation_types(schema: GraphQLSchema) -> bool:
102+
"""Check whether a schema uses the default root operation type names.
95103
96104
GraphQL schema define root types for each type of operation. These types are the
97105
same as any other type and can be named in any manner, however there is a common
98-
naming convention:
106+
naming convention::
99107
100-
schema {
101-
query: Query
102-
mutation: Mutation
103-
subscription: Subscription
104-
}
108+
schema {
109+
query: Query
110+
mutation: Mutation
111+
subscription: Subscription
112+
}
105113
106-
When using this naming convention, the schema description can be omitted.
107-
"""
108-
query_type = schema.query_type
109-
if query_type and query_type.name != "Query":
110-
return False
111-
112-
mutation_type = schema.mutation_type
113-
if mutation_type and mutation_type.name != "Mutation":
114-
return False
114+
When using this naming convention, the schema description can be omitted so
115+
long as these names are only used for operation types.
115116
116-
subscription_type = schema.subscription_type
117-
return not subscription_type or subscription_type.name == "Subscription"
117+
Note however that if any of these default names are used elsewhere in the
118+
schema but not as a root operation type, the schema definition must still
119+
be printed to avoid ambiguity.
120+
"""
121+
return (
122+
schema.query_type is schema.get_type("Query")
123+
and schema.mutation_type is schema.get_type("Mutation")
124+
and schema.subscription_type is schema.get_type("Subscription")
125+
)
118126

119127

120128
def print_type(type_: GraphQLNamedType) -> str:

tests/utilities/test_build_ast_schema.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
from ..fixtures import big_schema_sdl # noqa: F401
4040
from ..star_wars_schema import star_wars_schema
41-
from ..utils import dedent
41+
from ..utils import dedent, viral_sdl
4242

4343
try:
4444
from typing import TypeAlias
@@ -1188,6 +1188,21 @@ def throws_on_unknown_types():
11881188
build_schema(sdl, assume_valid_sdl=True)
11891189
assert str(exc_info.value).endswith("Unknown type: 'UnknownType'.")
11901190

1191+
def correctly_processes_viral_schema():
1192+
schema = build_schema(viral_sdl)
1193+
query_type = schema.query_type
1194+
assert isinstance(query_type, GraphQLNamedType)
1195+
assert query_type.name == "Query"
1196+
virus_type = schema.get_type("Virus")
1197+
assert isinstance(virus_type, GraphQLNamedType)
1198+
assert virus_type.name == "Virus"
1199+
mutation_type = schema.get_type("Mutation")
1200+
assert isinstance(mutation_type, GraphQLNamedType)
1201+
assert mutation_type.name == "Mutation"
1202+
# Though the viral schema has a 'Mutation' type, it is not used for the
1203+
# 'mutation' operation.
1204+
assert schema.mutation_type is None
1205+
11911206
def describe_deepcopy_and_pickle(): # pragma: no cover
11921207
sdl = print_schema(star_wars_schema)
11931208

tests/utilities/test_print_schema.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
print_value,
2828
)
2929

30-
from ..utils import dedent
30+
from ..utils import dedent, viral_schema, viral_sdl
3131

3232

3333
def expect_printed_schema(schema: GraphQLSchema) -> str:
@@ -865,6 +865,10 @@ def prints_introspection_schema():
865865
''' # noqa: E501
866866
)
867867

868+
def prints_viral_schema_correctly():
869+
printed = print_schema(viral_schema)
870+
assert printed == viral_sdl
871+
868872

869873
def describe_print_value():
870874
def print_value_convenience_function():

tests/utils/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44
from .assert_matching_values import assert_matching_values
55
from .dedent import dedent
66
from .gen_fuzz_strings import gen_fuzz_strings
7+
from .viral_schema import viral_schema
8+
from .viral_sdl import viral_sdl
79

810
__all__ = [
911
"assert_matching_values",
1012
"assert_equal_awaitables_or_values",
1113
"dedent",
1214
"gen_fuzz_strings",
15+
"viral_schema",
16+
"viral_sdl",
1317
]

tests/utils/viral_schema.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from graphql import GraphQLSchema
2+
from graphql.type import (
3+
GraphQLField,
4+
GraphQLList,
5+
GraphQLNonNull,
6+
GraphQLObjectType,
7+
GraphQLString,
8+
)
9+
10+
__all__ = ["viral_schema"]
11+
12+
Mutation = GraphQLObjectType(
13+
"Mutation",
14+
{
15+
"name": GraphQLField(GraphQLNonNull(GraphQLString)),
16+
"geneSequence": GraphQLField(GraphQLNonNull(GraphQLString)),
17+
},
18+
)
19+
20+
Virus = GraphQLObjectType(
21+
"Virus",
22+
{
23+
"name": GraphQLField(GraphQLNonNull(GraphQLString)),
24+
"knownMutations": GraphQLField(
25+
GraphQLNonNull(GraphQLList(GraphQLNonNull(Mutation)))
26+
),
27+
},
28+
)
29+
30+
Query = GraphQLObjectType(
31+
"Query", {"viruses": GraphQLField(GraphQLList(GraphQLNonNull(Virus)))}
32+
)
33+
34+
viral_schema = GraphQLSchema(Query)

tests/utils/viral_sdl.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
__all__ = ["viral_sdl"]
2+
3+
viral_sdl = """
4+
schema {
5+
query: Query
6+
}
7+
8+
type Query {
9+
viruses: [Virus!]
10+
}
11+
12+
type Virus {
13+
name: String!
14+
knownMutations: [Mutation!]!
15+
}
16+
17+
type Mutation {
18+
name: String!
19+
geneSequence: String!
20+
}
21+
""".strip()

0 commit comments

Comments
 (0)