Skip to content

Commit 7c66e7f

Browse files
committed
Subscription root validation:
* Recur into inline fragments and fragment spread * Point to the faulty directive * Skip printing an entire selection set
1 parent 1d50a53 commit 7c66e7f

13 files changed

+107
-49
lines changed

crates/apollo-compiler/src/executable/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,6 @@ pub(crate) enum BuildError {
273273
SubscriptionUsesConditionalSelection {
274274
/// Name of the operation
275275
name: Option<Name>,
276-
/// Selection that specify @skip or @include directives.
277-
selection: Selection,
278276
},
279277

280278
#[error("{0}")]

crates/apollo-compiler/src/validation/mod.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -854,13 +854,8 @@ impl ToCliReport for DiagnosticData {
854854
format_args!("{field} is an introspection field"),
855855
);
856856
}
857-
ExecutableBuildError::SubscriptionUsesConditionalSelection {
858-
selection, ..
859-
} => {
860-
report.with_label_opt(
861-
self.location,
862-
format_args!("{selection} specifies @skip or @include condition"),
863-
);
857+
ExecutableBuildError::SubscriptionUsesConditionalSelection { .. } => {
858+
report.with_label_opt(self.location, "conditional directive used here");
864859
}
865860
ExecutableBuildError::ConflictingFieldType(inner) => {
866861
let ConflictingFieldType {

crates/apollo-compiler/src/validation/operation.rs

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub(crate) fn validate_subscription(
1313
let fields = super::selection::expand_selections(
1414
&document.fragments,
1515
std::iter::once(&operation.selection_set),
16+
Some((operation, diagnostics)),
1617
);
1718

1819
if fields.len() > 1 {
@@ -46,24 +47,6 @@ pub(crate) fn validate_subscription(
4647
},
4748
);
4849
}
49-
50-
// first rule validates that we only have single selection
51-
let has_conditional_selection =
52-
operation.selection_set.selections.iter().find(|selection| {
53-
selection
54-
.directives()
55-
.iter()
56-
.any(|d| matches!(d.name.as_str(), "skip" | "include"))
57-
});
58-
if let Some(conditional_selection) = has_conditional_selection {
59-
diagnostics.push(
60-
operation.location(),
61-
executable::BuildError::SubscriptionUsesConditionalSelection {
62-
name: operation.name.clone(),
63-
selection: conditional_selection.clone(),
64-
},
65-
);
66-
}
6750
}
6851
}
6952

crates/apollo-compiler/src/validation/selection.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,28 @@ impl<'a> FieldSelection<'a> {
6767
pub(crate) fn expand_selections<'doc>(
6868
fragments: &'doc IndexMap<Name, Node<executable::Fragment>>,
6969
selection_sets: impl Iterator<Item = &'doc executable::SelectionSet>,
70+
mut for_subscription_top_level: Option<(&executable::Operation, &mut DiagnosticList)>,
7071
) -> Vec<FieldSelection<'doc>> {
7172
let mut selections = vec![];
7273
let mut queue: VecDeque<&executable::SelectionSet> = selection_sets.collect();
7374
let mut seen_fragments = HashSet::with_hasher(ahash::RandomState::new());
7475

7576
while let Some(next_set) = queue.pop_front() {
7677
for selection in &next_set.selections {
78+
if let Some((operation, diagnostics)) = &mut for_subscription_top_level {
79+
if let Some(conditional_directive) = selection
80+
.directives()
81+
.iter()
82+
.find(|d| matches!(d.name.as_str(), "skip" | "include"))
83+
{
84+
diagnostics.push(
85+
conditional_directive.location(),
86+
executable::BuildError::SubscriptionUsesConditionalSelection {
87+
name: operation.name.clone(),
88+
},
89+
);
90+
}
91+
}
7792
match selection {
7893
executable::Selection::Field(field) => {
7994
selections.push(FieldSelection::new(&next_set.ty, field))
@@ -520,8 +535,11 @@ impl<'alloc, 's, 'doc> FieldsInSetCanMerge<'alloc, 's, 'doc> {
520535
&self,
521536
selection_sets: impl Iterator<Item = &'doc executable::SelectionSet>,
522537
) -> &'alloc [FieldSelection<'doc>] {
523-
self.alloc
524-
.alloc(expand_selections(&self.document.fragments, selection_sets))
538+
self.alloc.alloc(expand_selections(
539+
&self.document.fragments,
540+
selection_sets,
541+
None,
542+
))
525543
}
526544

527545
pub(crate) fn validate_operation(
Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
Error: subscription `ConditionalSub` can not specify @skip or @include on root fields
2-
╭─[ 0120_conditional_subscriptions.graphql:1:1 ]
2+
╭─[ 0120_conditional_subscriptions.graphql:2:12 ]
33
4-
1 │ ╭─▶ subscription ConditionalSub($condition: Boolean = true) {
5-
┆ ┆
6-
3 │ ├─▶ }
7-
│ │
8-
│ ╰─────── ticker @include(if: $condition) specifies @skip or @include condition
4+
2 │ ticker @include(if: $condition)
5+
│ ────────────┬───────────
6+
│ ╰───────────── conditional directive used here
97
───╯
108

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
Error: subscription `ConditionalInlineSub` can not specify @skip or @include on root fields
2-
╭─[ 0121_conditional_subscriptions_with_inline_fragment.graphql:1:1 ]
2+
╭─[ 0121_conditional_subscriptions_with_inline_fragment.graphql:2:9 ]
33
4-
1 │ ╭─▶ subscription ConditionalInlineSub($condition: Boolean = true) {
5-
┆ ┆
6-
5 │ ├─▶ }
7-
│ │
8-
│ ╰─────── ... @include(if: $condition) {
9-
ticker
10-
} specifies @skip or @include condition
4+
2 │ ... @include(if: $condition) {
5+
│ ────────────┬───────────
6+
│ ╰───────────── conditional directive used here
117
───╯
128

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
Error: subscription `ConditionalInlineSub` can not specify @skip or @include on root fields
2-
╭─[ 0122_conditional_subscriptions_with_named_fragment.graphql:1:1 ]
2+
╭─[ 0122_conditional_subscriptions_with_named_fragment.graphql:2:23 ]
33
4-
1 │ ╭─▶ subscription ConditionalInlineSub($condition: Boolean = true) {
5-
┆ ┆
6-
3 │ ├─▶ }
7-
│ │
8-
│ ╰─────── ...mySubscription @include(if: $condition) specifies @skip or @include condition
4+
2 │ ...mySubscription @include(if: $condition)
5+
│ ────────────┬───────────
6+
│ ╰───────────── conditional directive used here
97
───╯
108

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
subscription ConditionalInlineSub($condition: Boolean = true) {
2+
... {
3+
ticker @include(if: $condition)
4+
}
5+
}
6+
7+
type Query {
8+
hello: String
9+
}
10+
11+
type Subscription {
12+
ticker: String
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Error: subscription `ConditionalInlineSub` can not specify @skip or @include on root fields
2+
╭─[ 0123_conditional_subscriptions_inside_inline_fragment.graphql:3:16 ]
3+
4+
3 │ ticker @include(if: $condition)
5+
│ ────────────┬───────────
6+
│ ╰───────────── conditional directive used here
7+
───╯
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
subscription ConditionalInlineSub($condition: Boolean = true) {
2+
...mySubscription
3+
}
4+
5+
fragment mySubscription on Subscription {
6+
ticker @include(if: $condition)
7+
}
8+
9+
type Query {
10+
hello: String
11+
}
12+
13+
type Subscription {
14+
ticker: String
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Error: subscription `ConditionalInlineSub` can not specify @skip or @include on root fields
2+
╭─[ 0124_conditional_subscriptions_inside_named_fragment.graphql:6:12 ]
3+
4+
6 │ ticker @include(if: $condition)
5+
│ ────────────┬───────────
6+
│ ╰───────────── conditional directive used here
7+
───╯
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
subscription ConditionalInlineSub($condition: Boolean = true) {
2+
... {
3+
ticker @include(if: $condition)
4+
}
5+
}
6+
7+
type Query {
8+
hello: String
9+
}
10+
11+
type Subscription {
12+
ticker: String
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
subscription ConditionalInlineSub($condition: Boolean = true) {
2+
...mySubscription
3+
}
4+
5+
fragment mySubscription on Subscription {
6+
ticker @include(if: $condition)
7+
}
8+
9+
type Query {
10+
hello: String
11+
}
12+
13+
type Subscription {
14+
ticker: String
15+
}

0 commit comments

Comments
 (0)