Fix lookahead when query contains multiple fields (#393)

Fixes https://github.com/graphql-rust/juniper/issues/371.
This commit is contained in:
Christian Legnitto 2019-07-17 18:57:14 -07:00 committed by GitHub
parent 7bedea05ed
commit 5065eb2265
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 213 additions and 1 deletions

View 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);
}

View file

@ -2,3 +2,5 @@
mod codegen;
#[cfg(test)]
mod custom_scalar;
#[cfg(test)]
mod issue_371;

View file

@ -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(

View file

@ -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(|| {