Skip to content

Commit dc8a8d5

Browse files
authored
fix: Node<SchemaDefinition>::to_ast method to handle orphan extensions (#984)
1 parent 4dc19fa commit dc8a8d5

File tree

2 files changed

+59
-15
lines changed

2 files changed

+59
-15
lines changed

crates/apollo-compiler/src/schema/serialize.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,19 @@ impl Node<SchemaDefinition> {
4444
subscription,
4545
} = &**self;
4646
let extensions = self.extensions();
47-
let implict = description.is_none()
47+
let root_ops = |ext: Option<&ExtensionId>| -> Vec<Node<(OperationType, Name)>> {
48+
self.iter_root_operations()
49+
.filter(|(_, op)| op.origin.extension_id() == ext)
50+
.map(|(ty, op)| (ty, op.name.clone()).into())
51+
.collect()
52+
};
53+
let orphan_extensions = root_ops(None).is_empty();
54+
let implicit = if orphan_extensions {
55+
// The schema definition's root operations are all in extensions. That means the orphan
56+
// extension was enabled and the schema definition was implicit.
57+
true
58+
} else {
59+
description.is_none()
4860
&& directives.is_empty()
4961
&& extensions.is_empty()
5062
&& [
@@ -54,7 +66,7 @@ impl Node<SchemaDefinition> {
5466
]
5567
.into_iter()
5668
.all(|(root_operation, operation_type)| {
57-
// If there were no explict `schema` definition,
69+
// If there were no explicit `schema` definition,
5870
// what implicit root operation would we get for this operation type?
5971
let default_type_name = operation_type.default_type_name();
6072
let has_object = types
@@ -71,14 +83,9 @@ impl Node<SchemaDefinition> {
7183
// This can be removed after that validation rule is ported to high-level `Schema`.
7284
&& [query, mutation, subscription]
7385
.into_iter()
74-
.any(|op| op.is_some());
75-
let root_ops = |ext: Option<&ExtensionId>| -> Vec<Node<(OperationType, Name)>> {
76-
self.iter_root_operations()
77-
.filter(|(_, op)| op.origin.extension_id() == ext)
78-
.map(|(ty, op)| (ty, op.name.clone()).into())
79-
.collect()
86+
.any(|op| op.is_some())
8087
};
81-
if implict {
88+
if implicit {
8289
None
8390
} else {
8491
Some(ast::Definition::SchemaDefinition(self.same_location(

crates/apollo-compiler/tests/extensions.rs

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
11
use apollo_compiler::Schema;
22

3+
fn validate_schema(schema: Schema) {
4+
let validated_schema = schema.validate().unwrap();
5+
6+
// Test the `Schema::to_string()` with orphan extensions by parsing and validating the printed
7+
// schema.
8+
let printed_schema = validated_schema.to_string();
9+
Schema::builder()
10+
.adopt_orphan_extensions()
11+
.parse(printed_schema, "printed_schema.graphql")
12+
.build()
13+
.unwrap()
14+
.validate()
15+
.unwrap();
16+
}
17+
318
#[test]
419
fn test_orphan_extensions() {
520
let input = r#"
621
extend schema @dir { query: Q }
722
extend type Obj @dir { foo: String }
8-
directive @dir on SCHEMA | OBJECT
23+
extend interface I @dir { foo: String }
24+
extend union U @dir = Obj
25+
extend enum E @dir { FOO, BAR }
26+
extend input Input @dir { bar: String }
27+
directive @dir on SCHEMA | SCALAR | OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT
928
type Q { x: Int }
1029
"#;
1130

@@ -31,7 +50,7 @@ fn test_orphan_extensions() {
3150
.unwrap();
3251
assert!(schema2.schema_definition.directives.has("dir"));
3352
assert!(schema2.types["Obj"].directives().has("dir"));
34-
schema2.validate().unwrap();
53+
validate_schema(schema2);
3554
}
3655

3756
#[test]
@@ -47,7 +66,7 @@ fn test_orphan_extensions_schema_with_default_query_name() {
4766
.build()
4867
.unwrap();
4968

50-
schema.validate().unwrap();
69+
validate_schema(schema);
5170
}
5271

5372
#[test]
@@ -67,7 +86,7 @@ fn test_orphan_extensions_schema_def_with_extensions() {
6786
.build()
6887
.unwrap();
6988

70-
schema.validate().unwrap();
89+
validate_schema(schema);
7190
}
7291

7392
#[test]
@@ -108,7 +127,7 @@ fn test_orphan_schema_extension_with_root_type_disables_implicit_root_types() {
108127
.unwrap();
109128

110129
assert!(schema.schema_definition.mutation.is_none());
111-
schema.validate().unwrap();
130+
validate_schema(schema);
112131
}
113132

114133
#[test]
@@ -126,7 +145,25 @@ fn test_orphan_schema_extension_without_root_type_enables_implicit_root_types()
126145
.unwrap();
127146

128147
assert!(schema.schema_definition.query.is_some());
129-
schema.validate().unwrap();
148+
validate_schema(schema);
149+
}
150+
151+
#[test]
152+
fn test_orphan_schema_extension_with_directive_application() {
153+
let input = r#"
154+
directive @something on SCHEMA
155+
extend schema @something { query: Query }
156+
type Query { field: Int }
157+
"#;
158+
159+
let schema = Schema::builder()
160+
.adopt_orphan_extensions()
161+
.parse(input, "schema.graphql")
162+
.build()
163+
.unwrap();
164+
165+
assert!(schema.schema_definition.query.is_some());
166+
validate_schema(schema);
130167
}
131168

132169
#[test]

0 commit comments

Comments
 (0)