Skip to content

Commit 00fdbbf

Browse files
authored
Merge pull request #181 from graphql-rust/type-refining-fragments
Implement type-refining fragments (fixes #154)
2 parents 6c5c3db + 3b5022f commit 00fdbbf

File tree

8 files changed

+329
-74
lines changed

8 files changed

+329
-74
lines changed

CHANGELOG.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,40 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1010
### Added
1111

1212
- The CLI can now optionally format the generated code with rustfmt (enable the `rustfmt` feature).
13+
- When deriving, the generated module now has the same visibility (private, `pub`, `pub(crate)` or `crate`) as the struct under derive.
14+
- Codegen now supports type-refining fragments, i.e. fragments on interfaces or unions that only apply to one of the variants. Example:
15+
16+
```graphql
17+
type Pie {
18+
diameter: Integer
19+
name: String
20+
}
21+
22+
type Sandwich {
23+
length: Float
24+
ingredients: [String]
25+
}
26+
27+
union Food = Sandwich | Pie
28+
29+
type Query {
30+
lunch: Food
31+
}
32+
33+
fragment PieName on Pie {
34+
name
35+
}
36+
37+
query Test {
38+
lunch {
39+
...PieName
40+
...on Sandwich {
41+
length
42+
}
43+
}
44+
}
45+
46+
```
1347

1448
### Changed
1549

graphql_client/tests/interfaces.rs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const RESPONSE: &'static str = include_str!("interfaces/interface_response.json"
1111
#[graphql(
1212
query_path = "tests/interfaces/interface_query.graphql",
1313
schema_path = "tests/interfaces/interface_schema.graphql",
14-
response_derives = "Debug, PartialEq",
14+
response_derives = "Debug, PartialEq"
1515
)]
1616
pub struct InterfaceQuery;
1717

@@ -50,15 +50,13 @@ fn interface_deserialization() {
5050
};
5151

5252
assert_eq!(response_data, expected);
53-
54-
assert_eq!(response_data.everything.map(|names| names.len()), Some(4));
5553
}
5654

5755
#[derive(GraphQLQuery)]
5856
#[graphql(
5957
query_path = "tests/interfaces/interface_not_on_everything_query.graphql",
6058
schema_path = "tests/interfaces/interface_schema.graphql",
61-
response_derives = "Debug",
59+
response_derives = "Debug"
6260
)]
6361
pub struct InterfaceNotOnEverythingQuery;
6462

@@ -84,7 +82,7 @@ fn interface_not_on_everything_deserialization() {
8482
#[graphql(
8583
query_path = "tests/interfaces/interface_with_fragment_query.graphql",
8684
schema_path = "tests/interfaces/interface_schema.graphql",
87-
response_derives = "Debug,PartialEq",
85+
response_derives = "Debug,PartialEq"
8886
)]
8987
pub struct InterfaceWithFragmentQuery;
9088

@@ -101,35 +99,37 @@ fn fragment_in_interface() {
10199
response_data,
102100
ResponseData {
103101
everything: Some(vec![
104-
RustMyQueryEverything {
102+
RustInterfaceWithFragmentQueryEverything {
105103
name: "Audrey Lorde".to_string(),
106104
public_status: PublicStatus {
107105
display_name: false,
108106
},
109-
on: RustMyQueryEverythingOn::Person(RustMyQueryEverythingOnPerson {
110-
birthday: Some("1934-02-18".to_string()),
111-
})
107+
on: RustInterfaceWithFragmentQueryEverythingOn::Person(
108+
RustInterfaceWithFragmentQueryEverythingOnPerson {
109+
birthday: Some("1934-02-18".to_string()),
110+
}
111+
)
112112
},
113-
RustMyQueryEverything {
113+
RustInterfaceWithFragmentQueryEverything {
114114
name: "Laïka".to_string(),
115115
public_status: PublicStatus { display_name: true },
116-
on: RustMyQueryEverythingOn::Dog(RustMyQueryEverythingOnDog {
117-
is_good_dog: true,
118-
})
116+
on: RustInterfaceWithFragmentQueryEverythingOn::Dog(
117+
RustInterfaceWithFragmentQueryEverythingOnDog { is_good_dog: true }
118+
)
119119
},
120-
RustMyQueryEverything {
120+
RustInterfaceWithFragmentQueryEverything {
121121
name: "Mozilla".to_string(),
122122
public_status: PublicStatus {
123123
display_name: false
124124
},
125-
on: RustMyQueryEverythingOn::Organization,
125+
on: RustInterfaceWithFragmentQueryEverythingOn::Organization,
126126
},
127-
RustMyQueryEverything {
127+
RustInterfaceWithFragmentQueryEverything {
128128
name: "Norbert".to_string(),
129129
public_status: PublicStatus { display_name: true },
130-
on: RustMyQueryEverythingOn::Dog(RustMyQueryEverythingOnDog {
131-
is_good_dog: true
132-
}),
130+
on: RustInterfaceWithFragmentQueryEverythingOn::Dog(
131+
RustInterfaceWithFragmentQueryEverythingOnDog { is_good_dog: true }
132+
),
133133
},
134134
])
135135
}

graphql_client/tests/interfaces/interface_with_fragment_query.graphql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ fragment PublicStatus on Named {
22
displayName
33
}
44

5-
query MyQuery {
5+
query InterfaceWithFragmentQuery {
66
everything {
7-
name
87
__typename
8+
name
99
...PublicStatus
1010
... on Dog {
1111
isGoodDog
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
fragment Birthday on Person {
2+
birthday
3+
}
4+
5+
query MyQuery {
6+
everything {
7+
__typename
8+
name
9+
... on Dog {
10+
isGoodDog
11+
}
12+
...Birthday
13+
... on Organization {
14+
industry
15+
}
16+
}
17+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#[macro_use]
2+
extern crate graphql_client;
3+
#[macro_use]
4+
extern crate serde_derive;
5+
extern crate serde;
6+
extern crate serde_json;
7+
8+
#[derive(GraphQLQuery)]
9+
#[graphql(
10+
query_path = "tests/interfaces/interface_with_type_refining_fragment_query.graphql",
11+
schema_path = "tests/interfaces/interface_schema.graphql",
12+
response_derives = "Debug, PartialEq"
13+
)]
14+
pub struct QueryOnInterface;
15+
16+
#[derive(GraphQLQuery)]
17+
#[graphql(
18+
query_path = "tests/unions/type_refining_fragment_on_union_query.graphql",
19+
schema_path = "tests/unions/union_schema.graphql",
20+
response_derives = "PartialEq, Debug"
21+
)]
22+
pub struct QueryOnUnion;
23+
24+
#[test]
25+
fn type_refining_fragment_on_union() {
26+
const RESPONSE: &'static str = include_str!("unions/union_query_response.json");
27+
28+
let response_data: query_on_union::ResponseData = serde_json::from_str(RESPONSE).unwrap();
29+
30+
let expected = query_on_union::ResponseData {
31+
names: Some(vec![
32+
query_on_union::RustMyQueryNames::Person(query_on_union::RustMyQueryNamesOnPerson {
33+
first_name: "Audrey".to_string(),
34+
last_name: Some("Lorde".to_string()),
35+
}),
36+
query_on_union::RustMyQueryNames::Dog(query_on_union::RustMyQueryNamesOnDog {
37+
name: "Laïka".to_string(),
38+
}),
39+
query_on_union::RustMyQueryNames::Organization(
40+
query_on_union::RustMyQueryNamesOnOrganization {
41+
title: "Mozilla".to_string(),
42+
},
43+
),
44+
query_on_union::RustMyQueryNames::Dog(query_on_union::RustMyQueryNamesOnDog {
45+
name: "Norbert".to_string(),
46+
}),
47+
]),
48+
};
49+
50+
assert_eq!(response_data, expected);
51+
}
52+
53+
#[test]
54+
fn type_refining_fragment_on_interface() {
55+
use query_on_interface::*;
56+
57+
const RESPONSE: &'static str = include_str!("interfaces/interface_response.json");
58+
59+
let response_data: query_on_interface::ResponseData = serde_json::from_str(RESPONSE).unwrap();
60+
61+
let expected = ResponseData {
62+
everything: Some(vec![
63+
RustMyQueryEverything {
64+
name: "Audrey Lorde".to_string(),
65+
on: RustMyQueryEverythingOn::Person(RustMyQueryEverythingOnPerson {
66+
birthday: Some("1934-02-18".to_string()),
67+
}),
68+
},
69+
RustMyQueryEverything {
70+
name: "Laïka".to_string(),
71+
on: RustMyQueryEverythingOn::Dog(RustMyQueryEverythingOnDog { is_good_dog: true }),
72+
},
73+
RustMyQueryEverything {
74+
name: "Mozilla".to_string(),
75+
on: RustMyQueryEverythingOn::Organization(RustMyQueryEverythingOnOrganization {
76+
industry: Industry::OTHER,
77+
}),
78+
},
79+
RustMyQueryEverything {
80+
name: "Norbert".to_string(),
81+
on: RustMyQueryEverythingOn::Dog(RustMyQueryEverythingOnDog { is_good_dog: true }),
82+
},
83+
]),
84+
};
85+
86+
assert_eq!(response_data, expected);
87+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
fragment DogName on Dog {
2+
name
3+
}
4+
5+
query MyQuery {
6+
names {
7+
__typename
8+
...DogName
9+
... on Person {
10+
firstName
11+
lastName
12+
}
13+
... on Organization {
14+
title
15+
}
16+
}
17+
}

0 commit comments

Comments
 (0)