Support fragments with nested types in lookahead
Fixes https://github.com/graphql-rust/juniper/issues/335
This commit is contained in:
parent
860aa8b419
commit
6ff551fcb0
2 changed files with 137 additions and 44 deletions
|
@ -101,6 +101,21 @@ pub struct LookAheadSelection<'a, S: 'a> {
|
|||
pub(super) children: Vec<ChildSelection<'a, S>>,
|
||||
}
|
||||
|
||||
impl<'a, S> Default for LookAheadSelection<'a, S>
|
||||
where
|
||||
S: ScalarValue,
|
||||
&'a S: ScalarRefValue<'a>,
|
||||
{
|
||||
fn default() -> Self {
|
||||
LookAheadSelection {
|
||||
name: "",
|
||||
alias: None,
|
||||
arguments: vec![],
|
||||
children: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> LookAheadSelection<'a, S>
|
||||
where
|
||||
S: ScalarValue,
|
||||
|
@ -167,8 +182,8 @@ where
|
|||
s: &'a Selection<'a, S>,
|
||||
vars: &'a Variables<S>,
|
||||
fragments: &'a HashMap<&'a str, &'a Fragment<'a, S>>,
|
||||
) -> LookAheadSelection<'a, S> {
|
||||
Self::build_from_selection_with_parent(s, None, vars, fragments).unwrap()
|
||||
) -> Option<LookAheadSelection<'a, S>> {
|
||||
Self::build_from_selection_with_parent(s, None, vars, fragments)
|
||||
}
|
||||
|
||||
fn build_from_selection_with_parent(
|
||||
|
@ -229,21 +244,29 @@ where
|
|||
Some(ret)
|
||||
}
|
||||
}
|
||||
Selection::FragmentSpread(ref fragment) if parent.is_some() => {
|
||||
Selection::FragmentSpread(ref fragment) => {
|
||||
let include = Self::should_include(fragment.item.directives.as_ref(), vars);
|
||||
if !include {
|
||||
return None;
|
||||
}
|
||||
let parent = parent.unwrap();
|
||||
let f = fragments.get(&fragment.item.name.item).unwrap();
|
||||
for c in f.selection_set.iter() {
|
||||
let s = LookAheadSelection::build_from_selection_with_parent(
|
||||
c,
|
||||
Some(parent),
|
||||
vars,
|
||||
fragments,
|
||||
);
|
||||
assert!(s.is_none());
|
||||
let f = fragments.get(&fragment.item.name.item).expect("a fragment");
|
||||
if let Some(parent) = parent {
|
||||
for c in f.selection_set.iter() {
|
||||
let s = LookAheadSelection::build_from_selection_with_parent(
|
||||
c,
|
||||
Some(parent),
|
||||
vars,
|
||||
fragments,
|
||||
);
|
||||
assert!(s.is_none());
|
||||
}
|
||||
} else {
|
||||
for c in f.selection_set.iter() {
|
||||
let s = LookAheadSelection::build_from_selection_with_parent(
|
||||
c, None, vars, fragments,
|
||||
);
|
||||
assert!(s.is_some());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -406,7 +429,8 @@ query Hero {
|
|||
&op.item.selection_set[0],
|
||||
&vars,
|
||||
&fragments,
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
let expected = LookAheadSelection {
|
||||
name: "hero",
|
||||
alias: None,
|
||||
|
@ -459,7 +483,8 @@ query Hero {
|
|||
&op.item.selection_set[0],
|
||||
&vars,
|
||||
&fragments,
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
let expected = LookAheadSelection {
|
||||
name: "hero",
|
||||
alias: Some("custom_hero"),
|
||||
|
@ -516,7 +541,8 @@ query Hero {
|
|||
&op.item.selection_set[0],
|
||||
&vars,
|
||||
&fragments,
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
let expected = LookAheadSelection {
|
||||
name: "hero",
|
||||
alias: None,
|
||||
|
@ -597,7 +623,8 @@ query Hero {
|
|||
&op.item.selection_set[0],
|
||||
&vars,
|
||||
&fragments,
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
let expected = LookAheadSelection {
|
||||
name: "hero",
|
||||
alias: None,
|
||||
|
@ -657,7 +684,8 @@ query Hero($episode: Episode) {
|
|||
&op.item.selection_set[0],
|
||||
&vars,
|
||||
&fragments,
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
let expected = LookAheadSelection {
|
||||
name: "hero",
|
||||
alias: None,
|
||||
|
@ -718,7 +746,8 @@ fragment commonFields on Character {
|
|||
&op.item.selection_set[0],
|
||||
&vars,
|
||||
&fragments,
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
let expected = LookAheadSelection {
|
||||
name: "hero",
|
||||
alias: None,
|
||||
|
@ -781,7 +810,8 @@ query Hero {
|
|||
&op.item.selection_set[0],
|
||||
&vars,
|
||||
&fragments,
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
let expected = LookAheadSelection {
|
||||
name: "hero",
|
||||
alias: None,
|
||||
|
@ -838,7 +868,8 @@ query Hero {
|
|||
&op.item.selection_set[0],
|
||||
&vars,
|
||||
&fragments,
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
let expected = LookAheadSelection {
|
||||
name: "hero",
|
||||
alias: None,
|
||||
|
@ -917,7 +948,8 @@ fragment comparisonFields on Character {
|
|||
&op.item.selection_set[0],
|
||||
&vars,
|
||||
&fragments,
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
let expected = LookAheadSelection {
|
||||
name: "hero",
|
||||
alias: None,
|
||||
|
@ -1069,6 +1101,7 @@ query Hero {
|
|||
&vars,
|
||||
&fragments,
|
||||
)
|
||||
.unwrap()
|
||||
.for_explicit_type("Human");
|
||||
let expected = ConcreteLookAheadSelection {
|
||||
name: "hero",
|
||||
|
@ -1191,4 +1224,59 @@ query Hero {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// https://github.com/graphql-rust/juniper/issues/335
|
||||
fn check_fragment_with_nesting() {
|
||||
let docs = parse_document_source::<DefaultScalarValue>(
|
||||
"
|
||||
query Hero {
|
||||
hero {
|
||||
...heroFriendNames
|
||||
}
|
||||
}
|
||||
|
||||
fragment heroFriendNames on Hero {
|
||||
friends { name }
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
let fragments = extract_fragments(&docs);
|
||||
|
||||
if let ::ast::Definition::Operation(ref op) = docs[0] {
|
||||
let vars = Variables::default();
|
||||
let look_ahead = LookAheadSelection::build_from_selection(
|
||||
&op.item.selection_set[0],
|
||||
&vars,
|
||||
&fragments,
|
||||
)
|
||||
.unwrap();
|
||||
let expected = LookAheadSelection {
|
||||
name: "hero",
|
||||
alias: None,
|
||||
arguments: Vec::new(),
|
||||
children: vec![ChildSelection {
|
||||
inner: LookAheadSelection {
|
||||
name: "friends",
|
||||
alias: None,
|
||||
arguments: Vec::new(),
|
||||
children: vec![ChildSelection {
|
||||
inner: LookAheadSelection {
|
||||
name: "name",
|
||||
alias: None,
|
||||
arguments: Vec::new(),
|
||||
children: Vec::new(),
|
||||
},
|
||||
applies_for: Applies::All,
|
||||
}],
|
||||
},
|
||||
applies_for: Applies::All,
|
||||
}],
|
||||
};
|
||||
assert_eq!(look_ahead, expected);
|
||||
} else {
|
||||
panic!("No Operation found");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -507,35 +507,40 @@ where
|
|||
});
|
||||
}
|
||||
|
||||
/// Construct a lookahead selection for the current selection
|
||||
/// Construct a lookahead selection for the current selection.
|
||||
///
|
||||
/// This allows to see the whole selection and preform operations
|
||||
/// affecting the childs
|
||||
/// This allows seeing the whole selection and perform operations
|
||||
/// affecting the children.
|
||||
pub fn look_ahead(&'a self) -> LookAheadSelection<'a, S> {
|
||||
self.parent_selection_set
|
||||
.map(|p| {
|
||||
LookAheadSelection::build_from_selection(&p[0], self.variables, self.fragments)
|
||||
})
|
||||
.unwrap_or_else(|| LookAheadSelection {
|
||||
name: self.current_type.innermost_concrete().name().unwrap_or(""),
|
||||
alias: None,
|
||||
arguments: Vec::new(),
|
||||
children: self
|
||||
.current_selection_set
|
||||
.map(|s| {
|
||||
s.iter()
|
||||
.map(|s| ChildSelection {
|
||||
inner: LookAheadSelection::build_from_selection(
|
||||
s,
|
||||
self.variables,
|
||||
self.fragments,
|
||||
),
|
||||
applies_for: Applies::All,
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_else(Vec::new),
|
||||
.filter(|s| s.is_some())
|
||||
.unwrap_or_else(|| {
|
||||
Some(LookAheadSelection {
|
||||
name: self.current_type.innermost_concrete().name().unwrap_or(""),
|
||||
alias: None,
|
||||
arguments: Vec::new(),
|
||||
children: self
|
||||
.current_selection_set
|
||||
.map(|s| {
|
||||
s.iter()
|
||||
.map(|s| ChildSelection {
|
||||
inner: LookAheadSelection::build_from_selection(
|
||||
s,
|
||||
self.variables,
|
||||
self.fragments,
|
||||
)
|
||||
.expect("a child selection"),
|
||||
applies_for: Applies::All,
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_else(Vec::new),
|
||||
})
|
||||
})
|
||||
.unwrap_or(LookAheadSelection::default())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue