Merge pull request #574 from oeed/master
Changed Lookahead to support multiple fields
This commit is contained in:
commit
1218664ed8
|
@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
- Add binary types to `ConstValue` and `Value`. [#569](https://github.com/async-graphql/async-graphql/issues/569)
|
- Add binary types to `ConstValue` and `Value`. [#569](https://github.com/async-graphql/async-graphql/issues/569)
|
||||||
|
- Changed Lookahead to support multiple fields. [#574](https://github.com/async-graphql/async-graphql/issues/547)
|
||||||
|
|
||||||
- Attach custom HTTP headers to the response when an error occurs. [#572](https://github.com/async-graphql/async-graphql/issues/572)
|
- Attach custom HTTP headers to the response when an error occurs. [#572](https://github.com/async-graphql/async-graphql/issues/572)
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{Name, Positioned, SelectionField};
|
||||||
/// A selection performed by a query.
|
/// A selection performed by a query.
|
||||||
pub struct Lookahead<'a> {
|
pub struct Lookahead<'a> {
|
||||||
fragments: &'a HashMap<Name, Positioned<FragmentDefinition>>,
|
fragments: &'a HashMap<Name, Positioned<FragmentDefinition>>,
|
||||||
field: Option<&'a Field>,
|
fields: Vec<&'a Field>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Lookahead<'a> {
|
impl<'a> Lookahead<'a> {
|
||||||
|
@ -16,28 +16,31 @@ impl<'a> Lookahead<'a> {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
fragments,
|
fragments,
|
||||||
field: Some(field),
|
fields: vec![field],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the first subfield of the selection set with the specified name. This will ignore
|
/// Get the field of the selection set with the specified name. This will ignore
|
||||||
/// aliases.
|
/// aliases.
|
||||||
///
|
///
|
||||||
/// For example, calling `.field("a")` on `{ a { b } }` will return a lookahead that
|
/// For example, calling `.field("a")` on `{ a { b } }` will return a lookahead that
|
||||||
/// represents `{ b }`.
|
/// represents `{ b }`.
|
||||||
pub fn field(&self, name: &str) -> Self {
|
pub fn field(&self, name: &str) -> Self {
|
||||||
|
let mut fields = Vec::new();
|
||||||
|
for field in &self.fields {
|
||||||
|
filter(&mut fields, self.fragments, &field.selection_set.node, name)
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
fragments: self.fragments,
|
fragments: self.fragments,
|
||||||
field: self
|
fields,
|
||||||
.field
|
|
||||||
.and_then(|field| find(self.fragments, &field.selection_set.node, name)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if field exists otherwise return false.
|
/// Returns true if field exists otherwise return false.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn exists(&self) -> bool {
|
pub fn exists(&self) -> bool {
|
||||||
self.field.is_some()
|
!self.fields.is_empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,34 +48,36 @@ impl<'a> From<SelectionField<'a>> for Lookahead<'a> {
|
||||||
fn from(selection_field: SelectionField<'a>) -> Self {
|
fn from(selection_field: SelectionField<'a>) -> Self {
|
||||||
Lookahead {
|
Lookahead {
|
||||||
fragments: selection_field.fragments,
|
fragments: selection_field.fragments,
|
||||||
field: Some(selection_field.field),
|
fields: vec![selection_field.field],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find<'a>(
|
fn filter<'a>(
|
||||||
|
fields: &mut Vec<&'a Field>,
|
||||||
fragments: &'a HashMap<Name, Positioned<FragmentDefinition>>,
|
fragments: &'a HashMap<Name, Positioned<FragmentDefinition>>,
|
||||||
selection_set: &'a SelectionSet,
|
selection_set: &'a SelectionSet,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> Option<&'a Field> {
|
) {
|
||||||
selection_set
|
for item in &selection_set.items {
|
||||||
.items
|
// doing this imperatively is a bit nasty, but using iterators would
|
||||||
.iter()
|
// require a boxed return type (I believe) as its recusive
|
||||||
.find_map(|item| match &item.node {
|
match &item.node {
|
||||||
Selection::Field(field) => {
|
Selection::Field(field) => {
|
||||||
if field.node.name.node == name {
|
if field.node.name.node == name {
|
||||||
Some(&field.node)
|
fields.push(&field.node)
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Selection::InlineFragment(fragment) => {
|
Selection::InlineFragment(fragment) => {
|
||||||
find(fragments, &fragment.node.selection_set.node, name)
|
filter(fields, fragments, &fragment.node.selection_set.node, name)
|
||||||
}
|
}
|
||||||
Selection::FragmentSpread(spread) => fragments
|
Selection::FragmentSpread(spread) => {
|
||||||
.get(&spread.node.fragment_name.node)
|
if let Some(fragment) = fragments.get(&spread.node.fragment_name.node) {
|
||||||
.and_then(|fragment| find(fragments, &fragment.node.selection_set.node, name)),
|
filter(fields, fragments, &fragment.node.selection_set.node, name)
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -104,12 +109,17 @@ mod tests {
|
||||||
if ctx.look_ahead().field("a").exists() {
|
if ctx.look_ahead().field("a").exists() {
|
||||||
// This is a query like `obj { a }`
|
// This is a query like `obj { a }`
|
||||||
assert_eq!(n, 1);
|
assert_eq!(n, 1);
|
||||||
} else if ctx.look_ahead().field("detail").field("c").exists() {
|
} else if ctx.look_ahead().field("detail").field("c").exists()
|
||||||
|
&& ctx.look_ahead().field("detail").field("d").exists()
|
||||||
|
{
|
||||||
// This is a query like `obj { detail { c } }`
|
// This is a query like `obj { detail { c } }`
|
||||||
assert_eq!(n, 2);
|
assert_eq!(n, 2);
|
||||||
|
} else if ctx.look_ahead().field("detail").field("c").exists() {
|
||||||
|
// This is a query like `obj { detail { c } }`
|
||||||
|
assert_eq!(n, 3);
|
||||||
} else {
|
} else {
|
||||||
// This query doesn't have `a`
|
// This query doesn't have `a`
|
||||||
assert_eq!(n, 3);
|
assert_eq!(n, 4);
|
||||||
}
|
}
|
||||||
MyObj {
|
MyObj {
|
||||||
a: 0,
|
a: 0,
|
||||||
|
@ -146,7 +156,7 @@ mod tests {
|
||||||
assert!(schema
|
assert!(schema
|
||||||
.execute(
|
.execute(
|
||||||
r#"{
|
r#"{
|
||||||
obj(n: 2) {
|
obj(n: 3) {
|
||||||
detail {
|
detail {
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
|
@ -159,7 +169,24 @@ mod tests {
|
||||||
assert!(schema
|
assert!(schema
|
||||||
.execute(
|
.execute(
|
||||||
r#"{
|
r#"{
|
||||||
obj(n: 3) {
|
obj(n: 2) {
|
||||||
|
detail {
|
||||||
|
d
|
||||||
|
}
|
||||||
|
|
||||||
|
detail {
|
||||||
|
c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.is_ok());
|
||||||
|
|
||||||
|
assert!(schema
|
||||||
|
.execute(
|
||||||
|
r#"{
|
||||||
|
obj(n: 4) {
|
||||||
b
|
b
|
||||||
}
|
}
|
||||||
}"#,
|
}"#,
|
||||||
|
@ -180,11 +207,30 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.is_ok());
|
.is_ok());
|
||||||
|
|
||||||
|
assert!(schema
|
||||||
|
.execute(
|
||||||
|
r#"{
|
||||||
|
obj(n: 3) {
|
||||||
|
... {
|
||||||
|
detail {
|
||||||
|
c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.is_ok());
|
||||||
|
|
||||||
assert!(schema
|
assert!(schema
|
||||||
.execute(
|
.execute(
|
||||||
r#"{
|
r#"{
|
||||||
obj(n: 2) {
|
obj(n: 2) {
|
||||||
... {
|
... {
|
||||||
|
detail {
|
||||||
|
d
|
||||||
|
}
|
||||||
|
|
||||||
detail {
|
detail {
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
|
@ -213,7 +259,7 @@ mod tests {
|
||||||
assert!(schema
|
assert!(schema
|
||||||
.execute(
|
.execute(
|
||||||
r#"{
|
r#"{
|
||||||
obj(n: 2) {
|
obj(n: 3) {
|
||||||
... A
|
... A
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,5 +272,29 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.is_ok());
|
.is_ok());
|
||||||
|
|
||||||
|
assert!(schema
|
||||||
|
.execute(
|
||||||
|
r#"{
|
||||||
|
obj(n: 2) {
|
||||||
|
... A
|
||||||
|
... B
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment A on MyObj {
|
||||||
|
detail {
|
||||||
|
d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment B on MyObj {
|
||||||
|
detail {
|
||||||
|
c
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.is_ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user