Skip to content

Commit 5ae930b

Browse files
authored
Fixes Issue 914 (#915)
* Add failing tests * Fix failing test * cargo fmt
1 parent 5225b9e commit 5ae930b

File tree

3 files changed

+136
-3
lines changed

3 files changed

+136
-3
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use juniper::*;
2+
3+
struct Query;
4+
5+
#[derive(GraphQLObject)]
6+
struct Foo {
7+
bar: Bar,
8+
}
9+
10+
#[derive(GraphQLObject)]
11+
struct Bar {
12+
a: i32,
13+
b: i32,
14+
baz: Baz,
15+
}
16+
17+
#[derive(GraphQLObject)]
18+
struct Baz {
19+
c: i32,
20+
d: i32,
21+
}
22+
23+
#[graphql_object]
24+
impl Query {
25+
fn foo() -> Foo {
26+
let baz = Baz { c: 1, d: 2 };
27+
let bar = Bar { a: 1, b: 2, baz };
28+
Foo { bar }
29+
}
30+
}
31+
32+
type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>;
33+
34+
#[tokio::test]
35+
async fn test_fragments_with_nested_objects_dont_override_previous_selections() {
36+
let query = r#"
37+
query Query {
38+
foo {
39+
...BarA
40+
...BarB
41+
...BazC
42+
...BazD
43+
}
44+
}
45+
46+
fragment BarA on Foo {
47+
bar {
48+
a
49+
}
50+
}
51+
52+
fragment BarB on Foo {
53+
bar {
54+
b
55+
}
56+
}
57+
58+
fragment BazC on Foo {
59+
bar {
60+
baz {
61+
c
62+
}
63+
}
64+
}
65+
66+
fragment BazD on Foo {
67+
bar {
68+
baz {
69+
d
70+
}
71+
}
72+
}
73+
"#;
74+
75+
let (async_value, errors) = juniper::execute(
76+
query,
77+
None,
78+
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
79+
&Variables::new(),
80+
&(),
81+
)
82+
.await
83+
.unwrap();
84+
assert_eq!(errors.len(), 0);
85+
86+
let (sync_value, errors) = juniper::execute_sync(
87+
query,
88+
None,
89+
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
90+
&Variables::new(),
91+
&(),
92+
)
93+
.unwrap();
94+
assert_eq!(errors.len(), 0);
95+
96+
assert_eq!(async_value, sync_value);
97+
98+
let bar = async_value
99+
.as_object_value()
100+
.unwrap()
101+
.get_field_value("foo")
102+
.unwrap()
103+
.as_object_value()
104+
.unwrap()
105+
.get_field_value("bar")
106+
.unwrap()
107+
.as_object_value()
108+
.unwrap();
109+
assert!(bar.contains_field("a"), "Field a should be selected");
110+
assert!(bar.contains_field("b"), "Field b should be selected");
111+
112+
let baz = bar
113+
.get_field_value("baz")
114+
.unwrap()
115+
.as_object_value()
116+
.unwrap();
117+
assert!(baz.contains_field("c"), "Field c should be selected");
118+
assert!(baz.contains_field("d"), "Field d should be selected");
119+
}

integration_tests/juniper_tests/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,6 @@ mod issue_407;
1919
#[cfg(test)]
2020
mod issue_500;
2121
#[cfg(test)]
22+
mod issue_914;
23+
#[cfg(test)]
2224
mod pre_parse;

juniper/src/value/object.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,26 @@ impl<S> Object<S> {
2828

2929
/// Add a new field with a value
3030
///
31-
/// If there is already a field with the same name the old value
32-
/// is returned
31+
/// If there is already a field for the given key
32+
/// any both values are objects, they are merged.
33+
///
34+
/// Otherwise the existing value is replaced and
35+
/// returned.
3336
pub fn add_field<K>(&mut self, k: K, value: Value<S>) -> Option<Value<S>>
3437
where
3538
K: Into<String>,
3639
for<'a> &'a str: PartialEq<K>,
3740
{
38-
self.key_value_list.insert(k.into(), value)
41+
let key: String = k.into();
42+
match (value, self.key_value_list.get_mut(&key)) {
43+
(Value::<S>::Object(obj_val), Some(Value::<S>::Object(existing_obj))) => {
44+
for (key, val) in obj_val.into_iter() {
45+
existing_obj.add_field::<String>(key, val);
46+
}
47+
None
48+
}
49+
(non_obj_val, _) => self.key_value_list.insert(key, non_obj_val),
50+
}
3951
}
4052

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

0 commit comments

Comments
 (0)