Skip to content

Commit d2a4fce

Browse files
authored
Merge pull request #229 from graphql-rust/cli-modules-reform
Make generated code less confusing.
2 parents 55fdc16 + 6a03a01 commit d2a4fce

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+548
-471
lines changed

CHANGELOG.md

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

88
## Unreleased
99

10+
## 0.7.0 - 2019-03-21
11+
12+
### Changed
13+
14+
- The `selected_operation` derive attribute is deprecated. **The name of the
15+
struct under derive now has to match one of the operations defined in the query
16+
file.**
17+
- The CLI now takes the schema path as a required keyword argument.
18+
- The CLI was revamped to generate separate modules for each operation in a given query file.
19+
20+
The idea now is that if you generate code for the following document:
21+
22+
```graphql
23+
# In my_query.graphql
24+
25+
query QueryA { .... }
26+
query QueryB { .... }
27+
mutation SomeMutation { ... }
28+
```
29+
30+
The CLI generated code will look like this:
31+
32+
```rust
33+
// In my_query.rs
34+
35+
pub struct QueryA;
36+
37+
pub mod query_a { ... }
38+
39+
pub struct QueryB;
40+
41+
pub mod query_b { ... }
42+
43+
pub struct SomeMutation;
44+
45+
pub mod some_mutation { ... }
46+
```
47+
48+
That way the operations don't live in the same module, there is no risk of names clashing anymore.
49+
1050
### Fixed
1151

1252
- Changes to query files will now always trigger code generation for the corresponding modules on the next build.

README.md

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ A typed GraphQL client library for Rust.
2929

3030
- In order to provide precise types for a response, graphql_client needs to read the query and the schema at compile-time.
3131

32-
To download the schema, you have multiple options. This projects provides a [CLI](https://github.com/graphql-rust/graphql-client/tree/master/graphql_client_cli), but there are also more mature tools like [apollo-cli](https://github.com/apollographql/apollo-cli). It does not matter which one you use, the resulting `schema.json` is the same.
32+
To download the schema, you have multiple options. This projects provides a [CLI](https://github.com/graphql-rust/graphql-client/tree/master/graphql_client_cli), however it does not matter what tool you use, the resulting `schema.json` is the same.
3333

3434
- We now have everything we need to derive Rust types for our query. This is achieved through a procedural macro, as in the following snippet:
3535

@@ -47,10 +47,10 @@ A typed GraphQL client library for Rust.
4747
schema_path = "tests/unions/union_schema.graphql",
4848
query_path = "tests/unions/union_query.graphql",
4949
)]
50-
pub struct MyQuery;
50+
pub struct UnionQuery;
5151
```
5252

53-
The `derive` will generate a module named `my_query` in this example - the name is the struct's name, but in snake case.
53+
The `derive` will generate a module named `union_query` in this example - the name is the struct's name, but in snake case.
5454

5555
That module contains all the struct and enum definitions necessary to deserialize a response to that query.
5656

@@ -74,16 +74,16 @@ A typed GraphQL client library for Rust.
7474
query_path = "tests/unions/union_query.graphql",
7575
response_derives = "Debug",
7676
)]
77-
pub struct MyQuery;
77+
pub struct UnionQuery;
7878
79-
fn perform_my_query(variables: my_query::Variables) -> Result<(), failure::Error> {
79+
fn perform_my_query(variables: union_query::Variables) -> Result<(), failure::Error> {
8080
8181
// this is the important line
82-
let request_body = MyQuery::build_query(variables);
82+
let request_body = UnionQuery::build_query(variables);
8383
8484
let client = reqwest::Client::new();
8585
let mut res = client.post("/graphql").json(&request_body).send()?;
86-
let response_body: Response<my_query::ResponseData> = res.json()?;
86+
let response_body: Response<union_query::ResponseData> = res.json()?;
8787
println!("{:#?}", response_body);
8888
Ok(())
8989
}
@@ -105,7 +105,7 @@ extern crate graphql_client;
105105
query_path = "tests/unions/union_query.graphql",
106106
response_derives = "Serialize,PartialEq",
107107
)]
108-
struct SearchQuery;
108+
struct UnionQuery;
109109
```
110110

111111
## Custom scalars
@@ -127,7 +127,7 @@ extern crate graphql_client;
127127
query_path = "tests/unions/union_query.graphql",
128128
deprecated = "warn"
129129
)]
130-
pub struct MyQuery;
130+
pub struct UnionQuery;
131131
```
132132

133133
Valid values are:
@@ -143,15 +143,20 @@ The default is `warn`.
143143

144144
You can write multiple operations in one query document (one `.graphql` file). You can then select one by naming the struct you `#[derive(GraphQLQuery)]` on with the same name as one of the operations. This is neat, as it allows sharing fragments between operations.
145145

146-
If you want to name the struct different from query name, you can use ``selected_operation`` argument like this.
146+
Note that the struct and the operation in the GraphQL file *must* have the same name. We enforce this to make the generated code more predictable.
147+
148+
```rust,skt-empty-main
149+
#[macro_use]
150+
extern crate graphql_client;
147151
148152
#[derive(GraphQLQuery)]
149153
#[graphql(
150154
schema_path = "tests/unions/union_schema.graphql",
151155
query_path = "tests/unions/union_query.graphql",
152156
selected_operation = "SearchQuery"
153157
)]
154-
pub struct MyQuery;
158+
pub struct UnionQuery;
159+
```
155160

156161
There is an example [in the tests](./tests/operation_selection).
157162

graphql_client/Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "graphql_client"
3-
version = "0.6.1"
3+
version = "0.7.0"
44
authors = ["Tom Houlé <[email protected]>"]
55
description = "Typed GraphQL requests and responses"
66
repository = "https://github.com/graphql-rust/graphql-client"
@@ -10,8 +10,7 @@ categories = ["network-programming", "web-programming", "wasm"]
1010

1111
[dependencies]
1212
failure = "0.1"
13-
graphql_query_derive = {path = "../graphql_query_derive", version = "0.6.1" }
14-
itertools = "0.7"
13+
graphql_query_derive = {path = "../graphql_query_derive", version = "0.7.0" }
1514
serde = "^1.0.78"
1615
serde_derive = "1.0"
1716
serde_json = "1.0"

graphql_client/src/lib.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#![deny(warnings)]
66
#![deny(missing_docs)]
77

8-
extern crate itertools;
98
extern crate serde;
109
#[macro_use]
1110
extern crate serde_derive;
@@ -20,8 +19,6 @@ pub use graphql_query_derive::*;
2019
use std::collections::HashMap;
2120
use std::fmt::{self, Display};
2221

23-
use itertools::Itertools;
24-
2522
/// A convenience trait that can be used to build a GraphQL request body.
2623
///
2724
/// This will be implemented for you by codegen in the normal case. It is implemented on the struct you place the derive on.
@@ -208,7 +205,16 @@ impl Display for Error {
208205
let path = self
209206
.path
210207
.as_ref()
211-
.map(|fragments| format!("{}", fragments.iter().format("/")))
208+
.map(|fragments| {
209+
fragments
210+
.iter()
211+
.fold(String::new(), |mut acc, item| {
212+
acc.push_str(&format!("{}/", item));
213+
acc
214+
})
215+
.trim_end_matches('/')
216+
.to_string()
217+
})
212218
.unwrap_or_else(|| "<query>".to_string());
213219

214220
// Get the location of the error. We'll use just the first location for this.

graphql_client/tests/custom_scalars.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type NetworkAddress = Ipv4Addr;
1616
query_path = "tests/custom_scalars/query.graphql",
1717
schema_path = "tests/custom_scalars/schema.graphql"
1818
)]
19-
pub struct CustomScalarsQuery;
19+
pub struct CustomScalarQuery;
2020

2121
#[test]
2222
fn custom_scalars() {
@@ -25,7 +25,7 @@ fn custom_scalars() {
2525
});
2626

2727
let valid_addr =
28-
serde_json::from_value::<custom_scalars_query::ResponseData>(valid_response).unwrap();
28+
serde_json::from_value::<custom_scalar_query::ResponseData>(valid_response).unwrap();
2929

3030
assert_eq!(
3131
valid_addr.address.unwrap(),
@@ -36,7 +36,5 @@ fn custom_scalars() {
3636
"address": "localhost",
3737
});
3838

39-
assert!(
40-
serde_json::from_value::<custom_scalars_query::ResponseData>(invalid_response).is_err()
41-
);
39+
assert!(serde_json::from_value::<custom_scalar_query::ResponseData>(invalid_response).is_err());
4240
}

graphql_client/tests/deprecation.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ fn deprecation_allow() {
3636
// as deprecated.
3737
#![deny(deprecated)]
3838
let _ = allow_deprecation::ResponseData {
39-
current_user: Some(allow_deprecation::TestCurrentUser {
39+
current_user: Some(allow_deprecation::AllowDeprecationCurrentUser {
4040
id: Some("abcd".to_owned()),
4141
name: Some("Angela Merkel".to_owned()),
4242
deprecated_with_reason: Some("foo".to_owned()),
@@ -48,7 +48,7 @@ fn deprecation_allow() {
4848
#[test]
4949
fn deprecation_deny() {
5050
let _ = deny_deprecation::ResponseData {
51-
current_user: Some(deny_deprecation::TestCurrentUser {
51+
current_user: Some(deny_deprecation::DenyDeprecationCurrentUser {
5252
id: Some("abcd".to_owned()),
5353
name: Some("Angela Merkel".to_owned()),
5454
// Notice the deprecated fields are not included here.
@@ -63,7 +63,7 @@ fn deprecation_deny() {
6363
fn deprecation_warn() {
6464
#![allow(deprecated)]
6565
let _ = warn_deprecation::ResponseData {
66-
current_user: Some(warn_deprecation::TestCurrentUser {
66+
current_user: Some(warn_deprecation::WarnDeprecationCurrentUser {
6767
id: Some("abcd".to_owned()),
6868
name: Some("Angela Merkel".to_owned()),
6969
deprecated_with_reason: Some("foo".to_owned()),

graphql_client/tests/deprecation/query.graphql

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,22 @@
1-
query Test {
1+
query DenyDeprecation {
2+
currentUser {
3+
name
4+
id
5+
deprecatedWithReason
6+
deprecatedNoReason
7+
}
8+
}
9+
10+
query AllowDeprecation {
11+
currentUser {
12+
name
13+
id
14+
deprecatedWithReason
15+
deprecatedNoReason
16+
}
17+
}
18+
19+
query WarnDeprecation {
220
currentUser {
321
name
422
id

graphql_client/tests/input_object_variables/input_object_variables_query.graphql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
query VariablesQuery($msg: Message) {
1+
query InputObjectVariablesQuery($msg: Message) {
22
echo(message: $msg) {
33
result
44
}

graphql_client/tests/input_object_variables/input_object_variables_query_defaults.graphql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
query VariablesQuery(
1+
query DefaultInputObjectVariablesQuery(
22
$msg: Message = { to: { email: "[email protected]" } }
33
) {
44
echo(message: $msg) {

graphql_client/tests/interfaces.rs

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,31 @@ fn interface_deserialization() {
2626

2727
let expected = ResponseData {
2828
everything: Some(vec![
29-
MyQueryEverything {
29+
InterfaceQueryEverything {
3030
name: "Audrey Lorde".to_string(),
31-
on: MyQueryEverythingOn::Person(MyQueryEverythingOnPerson {
31+
on: InterfaceQueryEverythingOn::Person(InterfaceQueryEverythingOnPerson {
3232
birthday: Some("1934-02-18".to_string()),
3333
}),
3434
},
35-
MyQueryEverything {
35+
InterfaceQueryEverything {
3636
name: "Laïka".to_string(),
37-
on: MyQueryEverythingOn::Dog(MyQueryEverythingOnDog { is_good_dog: true }),
37+
on: InterfaceQueryEverythingOn::Dog(InterfaceQueryEverythingOnDog {
38+
is_good_dog: true,
39+
}),
3840
},
39-
MyQueryEverything {
41+
InterfaceQueryEverything {
4042
name: "Mozilla".to_string(),
41-
on: MyQueryEverythingOn::Organization(MyQueryEverythingOnOrganization {
42-
industry: Industry::OTHER,
43-
}),
43+
on: InterfaceQueryEverythingOn::Organization(
44+
InterfaceQueryEverythingOnOrganization {
45+
industry: Industry::OTHER,
46+
},
47+
),
4448
},
45-
MyQueryEverything {
49+
InterfaceQueryEverything {
4650
name: "Norbert".to_string(),
47-
on: MyQueryEverythingOn::Dog(MyQueryEverythingOnDog { is_good_dog: true }),
51+
on: InterfaceQueryEverythingOn::Dog(InterfaceQueryEverythingOnDog {
52+
is_good_dog: true,
53+
}),
4854
},
4955
]),
5056
};
@@ -56,7 +62,7 @@ fn interface_deserialization() {
5662
#[graphql(
5763
query_path = "tests/interfaces/interface_not_on_everything_query.graphql",
5864
schema_path = "tests/interfaces/interface_schema.graphql",
59-
response_derives = "Debug"
65+
response_derives = "Debug,PartialEq"
6066
)]
6167
pub struct InterfaceNotOnEverythingQuery;
6268

@@ -65,15 +71,43 @@ const RESPONSE_NOT_ON_EVERYTHING: &'static str =
6571

6672
#[test]
6773
fn interface_not_on_everything_deserialization() {
68-
println!("{:?}", RESPONSE);
74+
use interface_not_on_everything_query::*;
75+
6976
let response_data: interface_not_on_everything_query::ResponseData =
7077
serde_json::from_str(RESPONSE_NOT_ON_EVERYTHING).unwrap();
7178

72-
println!("{:?}", response_data);
79+
let expected = ResponseData {
80+
everything: Some(vec![
81+
InterfaceNotOnEverythingQueryEverything {
82+
name: "Audrey Lorde".to_string(),
83+
on: InterfaceNotOnEverythingQueryEverythingOn::Person(
84+
InterfaceNotOnEverythingQueryEverythingOnPerson {
85+
birthday: Some("1934-02-18".to_string()),
86+
},
87+
),
88+
},
89+
InterfaceNotOnEverythingQueryEverything {
90+
name: "Laïka".to_string(),
91+
on: InterfaceNotOnEverythingQueryEverythingOn::Dog,
92+
},
93+
InterfaceNotOnEverythingQueryEverything {
94+
name: "Mozilla".to_string(),
95+
on: InterfaceNotOnEverythingQueryEverythingOn::Organization(
96+
InterfaceNotOnEverythingQueryEverythingOnOrganization {
97+
industry: Industry::OTHER,
98+
},
99+
),
100+
},
101+
InterfaceNotOnEverythingQueryEverything {
102+
name: "Norbert".to_string(),
103+
on: InterfaceNotOnEverythingQueryEverythingOn::Dog,
104+
},
105+
]),
106+
};
73107

74-
let expected = r##"ResponseData { everything: Some([MyQueryEverything { name: "Audrey Lorde", on: Person(MyQueryEverythingOnPerson { birthday: Some("1934-02-18") }) }, MyQueryEverything { name: "Laïka", on: Dog }, MyQueryEverything { name: "Mozilla", on: Organization(MyQueryEverythingOnOrganization { industry: OTHER }) }, MyQueryEverything { name: "Norbert", on: Dog }]) }"##;
108+
// let expected = r##"ResponseData { everything: Some([InterfaceQueryEverything { name: "Audrey Lorde", on: Person(InterfaceQueryEverythingOnPerson { birthday: Some("1934-02-18") }) }, InterfaceQueryEverything { name: "Laïka", on: Dog }, InterfaceQueryEverything { name: "Mozilla", on: Organization(InterfaceQueryEverythingOnOrganization { industry: OTHER }) }, InterfaceQueryEverything { name: "Norbert", on: Dog }]) }"##;
75109

76-
assert_eq!(format!("{:?}", response_data), expected);
110+
assert_eq!(response_data, expected);
77111

78112
assert_eq!(response_data.everything.map(|names| names.len()), Some(4));
79113
}

0 commit comments

Comments
 (0)