Improve visitability of lookahead types. (#431)

I've added methods which allow Juniper users to visit all nodes of a lookahead tree so that they can be used for query generation.
This commit is contained in:
James Harton 2019-09-30 14:00:45 +13:00 committed by Christian Legnitto
parent b61aa900b1
commit 5be66654a9
2 changed files with 105 additions and 5 deletions

View file

@ -1,10 +1,11 @@
# master
- Add ability to parse 'subscription'
- Improve lookahead visitability.
- Add ability to parse 'subscription'.
# [[0.13.1] 2019-07-29](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.13.1)
- Fix a regression when using lookaheads with fragments containing nested types [#404](https://github.com/graphql-rust/juniper/pull/404)
- Fix a regression when using lookaheads with fragments containing nested types [#404](https://github.com/graphql-rust/juniper/pull/404)
- Allow `mut` arguments for resolver functions in `#[object]` macros [#402](https://github.com/graphql-rust/juniper/pull/402)
@ -15,7 +16,7 @@
See [#345](https://github.com/graphql-rust/juniper/pull/345).
The newtype pattern can now be used with the `GraphQLScalarValue` custom derive
to easily implement custom scalar values that just wrap another scalar,
to easily implement custom scalar values that just wrap another scalar,
similar to serdes `#[serde(transparent)]` functionality.
Example:
@ -34,7 +35,7 @@ struct UserId(i32);
### object macro
The `graphql_object!` macro is deprecated and will be removed in the future.
The `graphql_object!` macro is deprecated and will be removed in the future.
It is replaced by the new [object](https://docs.rs/juniper/latest/juniper/macro.object.html) procedural macro.
[#333](https://github.com/graphql-rust/juniper/pull/333)
@ -53,7 +54,7 @@ This should not have any impact on your code, since juniper already was 2018 com
- The `GraphQLType` impl for () was removed to improve compile time safefty. [#355](https://github.com/graphql-rust/juniper/pull/355)
- The `ScalarValue` custom derive has been renamed to `GraphQLScalarValue`.
- Added built-in support for the canonical schema introspection query via
`juniper::introspect()`.
`juniper::introspect()`.
[#307](https://github.com/graphql-rust/juniper/issues/307)
- Fix introspection query validity
The DirectiveLocation::InlineFragment had an invalid literal value,

View file

@ -88,6 +88,11 @@ where
}
}
/// The argument's name
pub fn name(&'a self) -> &str {
&self.name
}
/// The value of the argument
pub fn value(&'a self) -> &LookAheadValue<'a, S> {
&self.value
@ -347,6 +352,12 @@ pub trait LookAheadMethods<S> {
self.select_child(name).is_some()
}
/// Does the current node have any arguments?
fn has_arguments(&self) -> bool;
/// Does the current node have any children?
fn has_children(&self) -> bool;
/// Get the top level arguments for the current selection
fn arguments(&self) -> &[LookAheadArgument<S>];
@ -354,6 +365,9 @@ pub trait LookAheadMethods<S> {
fn argument(&self, name: &str) -> Option<&LookAheadArgument<S>> {
self.arguments().iter().find(|a| a.name == name)
}
/// Get the top level children for the current selection
fn child_names(&self) -> Vec<&str>;
}
impl<'a, S> LookAheadMethods<S> for ConcreteLookAheadSelection<'a, S> {
@ -368,6 +382,21 @@ impl<'a, S> LookAheadMethods<S> for ConcreteLookAheadSelection<'a, S> {
fn arguments(&self) -> &[LookAheadArgument<S>] {
&self.arguments
}
fn child_names(&self) -> Vec<&str> {
self.children
.iter()
.map(|c| c.alias.unwrap_or(c.name))
.collect()
}
fn has_arguments(&self) -> bool {
!self.arguments.is_empty()
}
fn has_children(&self) -> bool {
!self.children.is_empty()
}
}
impl<'a, S> LookAheadMethods<S> for LookAheadSelection<'a, S> {
@ -385,6 +414,21 @@ impl<'a, S> LookAheadMethods<S> for LookAheadSelection<'a, S> {
fn arguments(&self) -> &[LookAheadArgument<S>] {
&self.arguments
}
fn child_names(&self) -> Vec<&str> {
self.children
.iter()
.map(|c| c.inner.alias.unwrap_or(c.inner.name))
.collect()
}
fn has_arguments(&self) -> bool {
!self.arguments.is_empty()
}
fn has_children(&self) -> bool {
!self.children.is_empty()
}
}
#[cfg(test)]
@ -1399,4 +1443,59 @@ fragment heroFriendNames on Hero {
panic!("No Operation found");
}
}
#[test]
fn check_visitability() {
let docs = parse_document_source::<DefaultScalarValue>(
"
query Hero {
hero(episode: EMPIRE) {
name
friends {
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();
assert_eq!(look_ahead.field_name(), "hero");
assert!(look_ahead.has_arguments());
let args = look_ahead.arguments();
assert_eq!(args[0].name(), "episode");
assert_eq!(args[0].value(), &LookAheadValue::Enum("EMPIRE"));
assert!(look_ahead.has_children());
assert_eq!(look_ahead.child_names(), vec!["name", "friends"]);
let child0 = look_ahead.select_child("name").unwrap();
assert_eq!(child0.field_name(), "name");
assert!(!child0.has_arguments());
assert!(!child0.has_children());
let child1 = look_ahead.select_child("friends").unwrap();
assert_eq!(child1.field_name(), "friends");
assert!(!child1.has_arguments());
assert!(child1.has_children());
assert_eq!(child1.child_names(), vec!["name"]);
let child2 = child1.select_child("name").unwrap();
assert!(!child2.has_arguments());
assert!(!child2.has_children());
} else {
panic!("No Operation found");
}
}
}