Skip to content

Commit 2008960

Browse files
Fix #500 (crash from look_ahead on nested fragments) (#800)
1 parent eca049a commit 2008960

File tree

5 files changed

+118
-22
lines changed

5 files changed

+118
-22
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use juniper::*;
2+
3+
struct Query;
4+
5+
#[juniper::graphql_object]
6+
impl Query {
7+
fn users(executor: &Executor) -> Vec<User> {
8+
executor.look_ahead();
9+
10+
vec![User {
11+
city: City {
12+
country: Country { id: 1 },
13+
},
14+
}]
15+
}
16+
}
17+
18+
struct User {
19+
city: City,
20+
}
21+
22+
#[juniper::graphql_object]
23+
impl User {
24+
fn city(&self, executor: &Executor) -> &City {
25+
executor.look_ahead();
26+
&self.city
27+
}
28+
}
29+
30+
struct City {
31+
country: Country,
32+
}
33+
34+
#[juniper::graphql_object]
35+
impl City {
36+
fn country(&self, executor: &Executor) -> &Country {
37+
executor.look_ahead();
38+
&self.country
39+
}
40+
}
41+
42+
struct Country {
43+
id: i32,
44+
}
45+
46+
#[juniper::graphql_object]
47+
impl Country {
48+
fn id(&self) -> i32 {
49+
self.id
50+
}
51+
}
52+
53+
type Schema = juniper::RootNode<'static, Query, EmptyMutation<()>, EmptySubscription<()>>;
54+
55+
#[tokio::test]
56+
async fn test_nested_fragments() {
57+
let query = r#"
58+
query Query {
59+
users {
60+
...UserFragment
61+
}
62+
}
63+
64+
fragment UserFragment on User {
65+
city {
66+
...CityFragment
67+
}
68+
}
69+
70+
fragment CityFragment on City {
71+
country {
72+
...CountryFragment
73+
}
74+
}
75+
76+
fragment CountryFragment on Country {
77+
id
78+
}
79+
"#;
80+
81+
let (_, errors) = juniper::execute(
82+
query,
83+
None,
84+
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
85+
&Variables::new(),
86+
&(),
87+
)
88+
.await
89+
.unwrap();
90+
91+
assert_eq!(errors.len(), 0);
92+
}

integration_tests/juniper_tests/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ mod infallible_as_field_error;
1212
mod issue_371;
1313
#[cfg(test)]
1414
mod issue_398;
15+
#[cfg(test)]
16+
mod issue_500;

juniper/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@
8484

8585
- When enabled, the optional `bson` integration now requires `bson-1.0.0`. ([#678](https://github.com/graphql-rust/juniper/pull/678))
8686

87+
- Fixed panic on `executor.look_ahead()` for nested fragments ([#500](https://github.com/graphql-rust/juniper/issues/500))
88+
8789
## Breaking Changes
8890

8991
- `GraphQLType` trait was split into 2 traits: ([#685](https://github.com/graphql-rust/juniper/pull/685))

juniper/src/executor/look_ahead.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ where
190190
Self::build_from_selection_with_parent(s, None, vars, fragments)
191191
}
192192

193-
fn build_from_selection_with_parent(
193+
pub(super) fn build_from_selection_with_parent(
194194
s: &'a Selection<'a, S>,
195195
parent: Option<&mut Self>,
196196
vars: &'a Variables<S>,

juniper/src/executor/mod.rs

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,7 @@ where
654654
};
655655
self.parent_selection_set
656656
.map(|p| {
657+
// Search the parent's fields to find this field within the set
657658
let found_field = p.iter().find(|&x| {
658659
match *x {
659660
Selection::Field(ref field) => {
@@ -672,31 +673,30 @@ where
672673
None
673674
}
674675
})
675-
.filter(|s| s.is_some())
676+
.flatten()
676677
.unwrap_or_else(|| {
677-
Some(LookAheadSelection {
678-
name: self.current_type.innermost_concrete().name().unwrap_or(""),
678+
// We didn't find a field in the parent's selection matching
679+
// this field, which means we're inside a FragmentSpread
680+
let mut ret = LookAheadSelection {
681+
name: field_name,
679682
alias: None,
680683
arguments: Vec::new(),
681-
children: self
682-
.current_selection_set
683-
.map(|s| {
684-
s.iter()
685-
.map(|s| ChildSelection {
686-
inner: LookAheadSelection::build_from_selection(
687-
&s,
688-
self.variables,
689-
self.fragments,
690-
)
691-
.expect("a child selection"),
692-
applies_for: Applies::All,
693-
})
694-
.collect()
695-
})
696-
.unwrap_or_else(Vec::new),
697-
})
684+
children: Vec::new(),
685+
};
686+
687+
// Add in all the children - this will mutate `ret`
688+
if let Some(selection_set) = self.current_selection_set {
689+
for c in selection_set {
690+
LookAheadSelection::build_from_selection_with_parent(
691+
c,
692+
Some(&mut ret),
693+
self.variables,
694+
self.fragments,
695+
);
696+
}
697+
}
698+
ret
698699
})
699-
.unwrap_or_default()
700700
}
701701

702702
/// Create new `OwnedExecutor` and clone all current data

0 commit comments

Comments
 (0)