Skip to content

Commit f3cb052

Browse files
fix: adopt_orphan_extensions + schema extensions + implicit root operation interaction (#907)
Building a schema with the `adopt_orphan_extensions` option enabled yields an error in the following simple case: ```graphql extend schema { query: Query } type Query { x: Int } ``` ``` Error: duplicate definitions for the `query` root operation type ╭─[schema.graphql:2:25] │ 2 │ extend schema { query: Query } │ ──────┬───── │ ╰─────── `query` redefined here ───╯ ``` Currently, it accidentally makes the assumption that a schema extension's root operations do not use a default name, which explains why [this test](https://github.com/apollographql/apollo-rs/blob/caf820d86a03db3650149cefe6157442c73d7e50/crates/apollo-compiler/tests/extensions.rs#L5-L10) currently passes but the failing test from [f3f95b8](f3f95b8) did not. This results in the `query` root operation type being added twice - once at the implicit step, and again when the orphan extensions are applied. In the case that `adopt_orphan_extensions` is enabled, we need to scan the orphan schema extensions for root operations that match the default name and _skip_ the implicit addition step if so, since the extensions will be applied in a subsequent step.
1 parent b0a9b49 commit f3cb052

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,12 +274,28 @@ impl SchemaBuilder {
274274
// This a macro rather than a closure to generate separate `static`s
275275
let schema_def = schema.schema_definition.make_mut();
276276
let mut has_implicit_root_operation = false;
277-
for (operation_type, root_operation) in [
277+
'root_operation_loop: for (operation_type, root_operation) in [
278278
(OperationType::Query, &mut schema_def.query),
279279
(OperationType::Mutation, &mut schema_def.mutation),
280280
(OperationType::Subscription, &mut schema_def.subscription),
281281
] {
282282
let name = operation_type.default_type_name();
283+
// If `adopt_orphan_extensions` is enabled, we should scan
284+
// each orphan schema extension for root operations. If we
285+
// see one, we should skip adding that particular implicit
286+
// root operation since the extensions will be applied
287+
// further down.
288+
if adopt_orphan_extensions {
289+
for ext in &orphan_extensions {
290+
for node in &ext.root_operations {
291+
let current_operation_type = node.as_ref().0;
292+
if current_operation_type == operation_type {
293+
continue 'root_operation_loop;
294+
}
295+
}
296+
}
297+
}
298+
283299
if schema.types.get(&name).is_some_and(|def| def.is_object()) {
284300
*root_operation = Some(name.into());
285301
has_implicit_root_operation = true

crates/apollo-compiler/tests/extensions.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,64 @@ fn test_orphan_extensions() {
3434
schema2.validate().unwrap();
3535
}
3636

37+
#[test]
38+
fn test_orphan_extensions_schema_with_default_query_name() {
39+
let input = r#"
40+
extend schema { query: Query }
41+
type Query { x: Int }
42+
"#;
43+
44+
let schema = Schema::builder()
45+
.adopt_orphan_extensions()
46+
.parse(input, "schema.graphql")
47+
.build()
48+
.unwrap();
49+
50+
schema.validate().unwrap();
51+
}
52+
53+
#[test]
54+
fn test_orphan_extensions_schema_def_with_extensions() {
55+
let input = r#"
56+
extend schema { query: Query }
57+
extend schema { subscription: S }
58+
schema { mutation: Mutation }
59+
type Query { x: Int }
60+
type Mutation { y: Int }
61+
type S { z: Int }
62+
"#;
63+
64+
let schema = Schema::builder()
65+
.adopt_orphan_extensions()
66+
.parse(input, "schema.graphql")
67+
.build()
68+
.unwrap();
69+
70+
schema.validate().unwrap();
71+
}
72+
73+
#[test]
74+
fn test_invalid_orphan_extensions_schema_def_with_duplicate_root_operation() {
75+
let input = r#"
76+
extend schema { query: Query }
77+
extend schema { subscription: S }
78+
schema { mutation: Mutation, query: AnotherQuery }
79+
type Query { x: Int }
80+
type Mutation { y: Int }
81+
type S { z: Int }
82+
type AnotherQuery { a: String }
83+
"#;
84+
85+
let invalid = Schema::builder()
86+
.adopt_orphan_extensions()
87+
.parse(input, "schema.graphql")
88+
.build()
89+
.unwrap_err();
90+
91+
let err = invalid.errors.to_string();
92+
assert!(err.contains("duplicate definitions for the `query` root operation type"));
93+
}
94+
3795
#[test]
3896
fn test_orphan_extensions_kind_mismatch() {
3997
let input = r#"

0 commit comments

Comments
 (0)