Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes Issue 914 #915

Merged
merged 3 commits into from
Apr 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
119 changes: 119 additions & 0 deletions integration_tests/juniper_tests/src/issue_914.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use juniper::*;

struct Query;

#[derive(GraphQLObject)]
struct Foo {
bar: Bar,
}

#[derive(GraphQLObject)]
struct Bar {
a: i32,
b: i32,
baz: Baz,
}

#[derive(GraphQLObject)]
struct Baz {
c: i32,
d: i32,
}

#[graphql_object]
impl Query {
fn foo() -> Foo {
let baz = Baz { c: 1, d: 2 };
let bar = Bar { a: 1, b: 2, baz };
Foo { bar }
}
}

type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>;

#[tokio::test]
async fn test_fragments_with_nested_objects_dont_override_previous_selections() {
let query = r#"
query Query {
foo {
...BarA
...BarB
...BazC
...BazD
}
}

fragment BarA on Foo {
bar {
a
}
}

fragment BarB on Foo {
bar {
b
}
}

fragment BazC on Foo {
bar {
baz {
c
}
}
}

fragment BazD on Foo {
bar {
baz {
d
}
}
}
"#;

let (async_value, errors) = juniper::execute(
query,
None,
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
&Variables::new(),
&(),
)
.await
.unwrap();
assert_eq!(errors.len(), 0);

let (sync_value, errors) = juniper::execute_sync(
query,
None,
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
&Variables::new(),
&(),
)
.unwrap();
assert_eq!(errors.len(), 0);

assert_eq!(async_value, sync_value);

let bar = async_value
.as_object_value()
.unwrap()
.get_field_value("foo")
.unwrap()
.as_object_value()
.unwrap()
.get_field_value("bar")
.unwrap()
.as_object_value()
.unwrap();
assert!(bar.contains_field("a"), "Field a should be selected");
assert!(bar.contains_field("b"), "Field b should be selected");

let baz = bar
.get_field_value("baz")
.unwrap()
.as_object_value()
.unwrap();
assert!(baz.contains_field("c"), "Field c should be selected");
assert!(baz.contains_field("d"), "Field d should be selected");
}
2 changes: 2 additions & 0 deletions integration_tests/juniper_tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ mod issue_407;
#[cfg(test)]
mod issue_500;
#[cfg(test)]
mod issue_914;
#[cfg(test)]
mod pre_parse;
18 changes: 15 additions & 3 deletions juniper/src/value/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,26 @@ impl<S> Object<S> {

/// Add a new field with a value
///
/// If there is already a field with the same name the old value
/// is returned
/// If there is already a field for the given key
/// any both values are objects, they are merged.
///
/// Otherwise the existing value is replaced and
/// returned.
pub fn add_field<K>(&mut self, k: K, value: Value<S>) -> Option<Value<S>>
where
K: Into<String>,
for<'a> &'a str: PartialEq<K>,
{
self.key_value_list.insert(k.into(), value)
let key: String = k.into();
match (value, self.key_value_list.get_mut(&key)) {
(Value::<S>::Object(obj_val), Some(Value::<S>::Object(existing_obj))) => {
for (key, val) in obj_val.into_iter() {
existing_obj.add_field::<String>(key, val);
}
None
}
(non_obj_val, _) => self.key_value_list.insert(key, non_obj_val),
}
}

/// Check if the object already contains a field with the given name
Expand Down