Fix lookahead when query contains multiple fields (#393)
Fixes https://github.com/graphql-rust/juniper/issues/371.
This commit is contained in:
parent
7bedea05ed
commit
5065eb2265
4 changed files with 213 additions and 1 deletions
124
integration_tests/juniper_tests/src/issue_371.rs
Normal file
124
integration_tests/juniper_tests/src/issue_371.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
// Original author of this test is <https://github.com/davidpdrsn>.
|
||||
use juniper::*;
|
||||
|
||||
pub struct Context;
|
||||
|
||||
impl juniper::Context for Context {}
|
||||
|
||||
pub struct Query;
|
||||
|
||||
graphql_object!(Query: Context |&self| {
|
||||
field users(&executor) -> Vec<User> {
|
||||
let lh = executor.look_ahead();
|
||||
assert_eq!(lh.field_name(), "users");
|
||||
vec![User]
|
||||
}
|
||||
|
||||
field countries(&executor) -> Vec<Country> {
|
||||
let lh = executor.look_ahead();
|
||||
assert_eq!(lh.field_name(), "countries");
|
||||
vec![Country]
|
||||
}
|
||||
});
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct User;
|
||||
|
||||
graphql_object!(User: Context |&self| {
|
||||
field id() -> i32 {
|
||||
1
|
||||
}
|
||||
});
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Country;
|
||||
|
||||
graphql_object!(Country: Context |&self| {
|
||||
field id() -> i32 {
|
||||
2
|
||||
}
|
||||
});
|
||||
|
||||
type Schema = juniper::RootNode<'static, Query, EmptyMutation<Context>>;
|
||||
|
||||
#[test]
|
||||
fn users() {
|
||||
let ctx = Context;
|
||||
|
||||
let query = r#"{ users { id } }"#;
|
||||
|
||||
let (_, errors) = juniper::execute(
|
||||
query,
|
||||
None,
|
||||
&Schema::new(Query, EmptyMutation::<Context>::new()),
|
||||
&juniper::Variables::new(),
|
||||
&ctx,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(errors.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn countries() {
|
||||
let ctx = Context;
|
||||
|
||||
let query = r#"{ countries { id } }"#;
|
||||
|
||||
let (_, errors) = juniper::execute(
|
||||
query,
|
||||
None,
|
||||
&Schema::new(Query, EmptyMutation::new()),
|
||||
&juniper::Variables::new(),
|
||||
&ctx,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(errors.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn both() {
|
||||
let ctx = Context;
|
||||
|
||||
let query = r#"
|
||||
{
|
||||
countries { id }
|
||||
users { id }
|
||||
}
|
||||
"#;
|
||||
|
||||
let (_, errors) = juniper::execute(
|
||||
query,
|
||||
None,
|
||||
&Schema::new(Query, EmptyMutation::<Context>::new()),
|
||||
&juniper::Variables::new(),
|
||||
&ctx,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(errors.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn both_in_different_order() {
|
||||
let ctx = Context;
|
||||
|
||||
let query = r#"
|
||||
{
|
||||
users { id }
|
||||
countries { id }
|
||||
}
|
||||
"#;
|
||||
|
||||
let (_, errors) = juniper::execute(
|
||||
query,
|
||||
None,
|
||||
&Schema::new(Query, EmptyMutation::<Context>::new()),
|
||||
&juniper::Variables::new(),
|
||||
&ctx,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(errors.len(), 0);
|
||||
}
|
|
@ -2,3 +2,5 @@
|
|||
mod codegen;
|
||||
#[cfg(test)]
|
||||
mod custom_scalar;
|
||||
#[cfg(test)]
|
||||
mod issue_371;
|
||||
|
|
|
@ -960,6 +960,73 @@ query Hero {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_query_with_multiple() {
|
||||
let docs = parse_document_source::<DefaultScalarValue>(
|
||||
"
|
||||
query HeroAndHuman {
|
||||
hero {
|
||||
id
|
||||
}
|
||||
human {
|
||||
name
|
||||
}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
let fragments = extract_fragments(&docs);
|
||||
|
||||
if let crate::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: "id",
|
||||
alias: None,
|
||||
arguments: Vec::new(),
|
||||
children: Vec::new(),
|
||||
},
|
||||
applies_for: Applies::All,
|
||||
}],
|
||||
};
|
||||
assert_eq!(look_ahead, expected);
|
||||
|
||||
let look_ahead = LookAheadSelection::build_from_selection(
|
||||
&op.item.selection_set[1],
|
||||
&vars,
|
||||
&fragments,
|
||||
)
|
||||
.unwrap();
|
||||
let expected = LookAheadSelection {
|
||||
name: "human",
|
||||
alias: None,
|
||||
arguments: Vec::new(),
|
||||
children: vec![ChildSelection {
|
||||
inner: LookAheadSelection {
|
||||
name: "name",
|
||||
alias: None,
|
||||
arguments: Vec::new(),
|
||||
children: Vec::new(),
|
||||
},
|
||||
applies_for: Applies::All,
|
||||
}],
|
||||
};
|
||||
assert_eq!(look_ahead, expected);
|
||||
} else {
|
||||
panic!("No Operation found");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_complex_query() {
|
||||
let docs = parse_document_source(
|
||||
|
|
|
@ -512,9 +512,28 @@ where
|
|||
/// This allows seeing the whole selection and perform operations
|
||||
/// affecting the children.
|
||||
pub fn look_ahead(&'a self) -> LookAheadSelection<'a, S> {
|
||||
let field_name = match self.field_path {
|
||||
FieldPath::Field(x, ..) => x,
|
||||
FieldPath::Root(_) => unreachable!(),
|
||||
};
|
||||
self.parent_selection_set
|
||||
.map(|p| {
|
||||
LookAheadSelection::build_from_selection(&p[0], self.variables, self.fragments)
|
||||
let p = p
|
||||
.iter()
|
||||
.find(|&x| {
|
||||
match *x {
|
||||
Selection::Field(ref field) => {
|
||||
let field = &field.item;
|
||||
// TODO: support excludes.
|
||||
let name = field.name.item;
|
||||
let alias = field.alias.as_ref().map(|a| a.item);
|
||||
alias.unwrap_or(name) == field_name
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
.expect("lookahead to find the field");
|
||||
LookAheadSelection::build_from_selection(&p, self.variables, self.fragments)
|
||||
})
|
||||
.filter(|s| s.is_some())
|
||||
.unwrap_or_else(|| {
|
||||
|
|
Loading…
Reference in a new issue