Skip to content

Commit c23b685

Browse files
authored
Merge pull request #175 from h-michael/code-gen
Generate all queries at onece
2 parents 00fdbbf + 5d898e4 commit c23b685

File tree

10 files changed

+308
-50
lines changed

10 files changed

+308
-50
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,16 @@ The default is `warn`.
128128

129129
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.
130130

131+
If you want to name the struct different from query name, you can use ``selected_operation`` argument like this.
132+
133+
#[derive(GraphQLQuery)]
134+
#[graphql(
135+
schema_path = "src/graphql/schema.json",
136+
query_path = "src/graphql/queries/my_query.graphql",
137+
selected_operation = "SearchQuery"
138+
)]
139+
pub struct MyQuery;
140+
131141
There is an example [in the tests](./tests/operation_selection).
132142

133143
## Documentation for the generated modules

graphql_client/tests/operation_selection.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ use graphql_client::GraphQLQuery;
1111
#[graphql(
1212
query_path = "tests/operation_selection/queries.graphql",
1313
schema_path = "tests/operation_selection/schema.graphql",
14-
response_derives = "Debug,PartialEq",
14+
response_derives = "Debug,PartialEq"
1515
)]
1616
pub struct Heights;
1717

1818
#[derive(GraphQLQuery)]
1919
#[graphql(
2020
query_path = "tests/operation_selection/queries.graphql",
2121
schema_path = "tests/operation_selection/schema.graphql",
22-
response_derives = "Debug,PartialEq",
22+
response_derives = "Debug,PartialEq"
2323
)]
2424
pub struct Echo;
2525

@@ -28,10 +28,19 @@ pub struct Echo;
2828
#[graphql(
2929
query_path = "tests/operation_selection/queries.graphql",
3030
schema_path = "tests/operation_selection/schema.graphql",
31-
response_derives = "Debug,PartialEq",
31+
response_derives = "Debug,PartialEq"
3232
)]
3333
pub struct Unrelated;
3434

35+
#[derive(GraphQLQuery)]
36+
#[graphql(
37+
query_path = "tests/operation_selection/queries.graphql",
38+
schema_path = "tests/operation_selection/schema.graphql",
39+
response_derives = "Debug,PartialEq",
40+
selected_operation = "Echo"
41+
)]
42+
pub struct SelectedOperation;
43+
3544
const HEIGHTS_RESPONSE: &'static str = r##"{"mountainHeight": 224, "buildingHeight": 12}"##;
3645
const ECHO_RESPONSE: &'static str = r##"{"echo": "tiramisù"}"##;
3746

@@ -42,6 +51,8 @@ fn operation_selection_works() {
4251
let heights_unrelated_response_data: unrelated::ResponseData =
4352
serde_json::from_str(HEIGHTS_RESPONSE).unwrap();
4453
let echo_response_data: echo::ResponseData = serde_json::from_str(ECHO_RESPONSE).unwrap();
54+
let selected_operation_response_data: selected_operation::ResponseData =
55+
serde_json::from_str(ECHO_RESPONSE).unwrap();
4556

4657
let _echo_variables = echo::Variables {
4758
msg: Some("hi".to_string()),
@@ -56,9 +67,14 @@ fn operation_selection_works() {
5667
mountain_name: Some("canigou".to_string()),
5768
};
5869

70+
let _selected_operation_variables = selected_operation::Variables {
71+
msg: Some("hi".to_string()),
72+
};
73+
5974
let expected_echo = echo::ResponseData {
6075
echo: Some("tiramisù".to_string()),
6176
};
77+
6278
let expected_heights = heights::ResponseData {
6379
mountain_height: Some(224),
6480
building_height: Some(12),
@@ -69,9 +85,17 @@ fn operation_selection_works() {
6985
building_height: Some(12),
7086
};
7187

88+
let expected_selected_operation = selected_operation::ResponseData {
89+
echo: Some("tiramisù".to_string()),
90+
};
91+
7292
assert_eq!(expected_echo, echo_response_data);
7393
assert_eq!(expected_heights, heights_response_data);
7494
assert_eq!(expected_heights_unrelated, heights_unrelated_response_data);
95+
assert_eq!(
96+
expected_selected_operation,
97+
selected_operation_response_data
98+
);
7599
}
76100

77101
#[test]

graphql_client_cli/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,61 @@
11
# GraphQL client CLI
22

33
This is still a WIP, the main use for it now is to download the `schema.json` from a GraphQL endpoint, which you can also do with [apollo-codegen](https://github.com/apollographql/apollo-cli).
4+
5+
## Install
6+
7+
```
8+
cargo install graphql-client-cli --force
9+
```
10+
11+
## introspect schema
12+
13+
```
14+
USAGE:
15+
graphql-client introspect-schema [OPTIONS] <schema_location>
16+
17+
FLAGS:
18+
-h, --help Prints help information
19+
-V, --version Prints version information
20+
21+
OPTIONS:
22+
--authorization <authorization>
23+
--output <output> Where to write the JSON for the introspected schema.
24+
25+
ARGS:
26+
<schema_location> The URL of a GraphQL endpoint to introspect.
27+
```
28+
29+
## generate client code
30+
31+
```
32+
USAGE:
33+
graphql-client generate [FLAGS] [OPTIONS] <query_path> <schema_path> <module_name> <output>
34+
35+
FLAGS:
36+
-h, --help Prints help information
37+
--no-formatting If you don't want to execute rustfmt to generated code, set this option. Default value is
38+
false. Formating feature is disabled as default installation.
39+
-V, --version Prints version information
40+
41+
OPTIONS:
42+
-a, --additional-derives <additional_derives>
43+
Additional derives that will be added to the generated structs and enums for the response and the variables.
44+
--additional-derives='Serialize,PartialEq'
45+
-d, --deprecation-strategy <deprecation_strategy>
46+
You can choose deprecation strategy from allow, deny, or warn. Default value is warn.
47+
48+
-o, --selected-operation <selected_operation>
49+
Name of target query. If you don't set this parameter, cli generate all queries in query file.
50+
51+
52+
ARGS:
53+
<query_path> Path to graphql query file.
54+
<schema_path> Path to graphql schema file.
55+
<module_name> Name of module.
56+
<output> Path you want to output to.
57+
```
58+
59+
If you want to use formatting feature, you should install like this.
60+
61+
`cargo install graphql-client-cli --features rustfmt --force`

graphql_client_cli/src/generate.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use syn;
99
pub fn generate_code(
1010
query_path: PathBuf,
1111
schema_path: PathBuf,
12-
selected_operation: String,
12+
module_name: String,
13+
selected_operation: Option<String>,
1314
additional_derives: Option<String>,
1415
deprecation_strategy: &Option<String>,
1516
no_formatting: bool,
@@ -36,7 +37,9 @@ pub fn generate_code(
3637
};
3738

3839
let options = GraphQLClientDeriveOptions {
39-
struct_name: selected_operation,
40+
operation_name: selected_operation,
41+
struct_name: None,
42+
module_name: Some(module_name),
4043
additional_derives,
4144
deprecation_strategy,
4245
module_visibility,

graphql_client_cli/src/main.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@ enum Cli {
4242
/// Path to graphql schema file.
4343
#[structopt(parse(from_os_str))]
4444
schema_path: PathBuf,
45-
/// Name of struct that is implementation target.
46-
selected_operation: String,
45+
/// Name of module.
46+
module_name: String,
47+
/// Name of target query. If you don't set this parameter, cli generate all queries in query file.
48+
#[structopt(short = "o", long = "selected-operation")]
49+
selected_operation: Option<String>,
4750
/// Additional derives that will be added to the generated structs and enums for the response and the variables.
4851
/// --additional-derives='Serialize,PartialEq'
4952
#[structopt(short = "a", long = "additional-derives")]
@@ -77,6 +80,7 @@ fn main() -> Result<(), failure::Error> {
7780
Cli::Generate {
7881
query_path,
7982
schema_path,
83+
module_name,
8084
selected_operation,
8185
additional_derives,
8286
deprecation_strategy,
@@ -86,6 +90,7 @@ fn main() -> Result<(), failure::Error> {
8690
} => generate::generate_code(
8791
query_path,
8892
schema_path,
93+
module_name,
8994
selected_operation,
9095
additional_derives,
9196
&deprecation_strategy,

graphql_client_codegen/src/codegen.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,31 @@ use failure;
33
use fragments::GqlFragment;
44
use graphql_parser::query;
55
use operations::Operation;
6-
use proc_macro2::TokenStream;
6+
use proc_macro2::{Ident, Span, TokenStream};
77
use query::QueryContext;
88
use schema;
99
use selection::Selection;
1010

1111
/// Selects the first operation matching `struct_name` or the first one. Returns `None` when the query document defines no operation.
1212
pub(crate) fn select_operation(query: &query::Document, struct_name: &str) -> Option<Operation> {
13+
let operations = all_operations(query);
14+
15+
operations
16+
.iter()
17+
.find(|op| op.name == struct_name)
18+
.map(|i| i.to_owned())
19+
.or_else(|| operations.iter().next().map(|i| i.to_owned()))
20+
}
21+
22+
pub(crate) fn all_operations(query: &query::Document) -> Vec<Operation> {
1323
let mut operations: Vec<Operation> = Vec::new();
1424

1525
for definition in &query.definitions {
1626
if let query::Definition::Operation(op) = definition {
1727
operations.push(op.into());
1828
}
1929
}
20-
2130
operations
22-
.iter()
23-
.find(|op| op.name == struct_name)
24-
.map(|i| i.to_owned())
25-
.or_else(|| operations.iter().next().map(|i| i.to_owned()))
2631
}
2732

2833
/// The main code generation function.
@@ -32,6 +37,7 @@ pub fn response_for_query(
3237
operation: &Operation,
3338
additional_derives: Option<String>,
3439
deprecation_strategy: deprecation::DeprecationStrategy,
40+
multiple_operation: bool,
3541
) -> Result<TokenStream, failure::Error> {
3642
let mut context = QueryContext::new(schema, deprecation_strategy);
3743

@@ -112,7 +118,8 @@ pub fn response_for_query(
112118
}
113119
}).collect();
114120
let fragment_definitions = fragment_definitions?;
115-
let variables_struct = operation.expand_variables(&context);
121+
let variables_struct =
122+
operation.expand_variables(&context, &operation.name, multiple_operation);
116123

117124
let input_object_definitions: Result<Vec<TokenStream>, _> = context
118125
.schema
@@ -141,6 +148,15 @@ pub fn response_for_query(
141148

142149
let response_derives = context.response_derives();
143150

151+
let respons_data_struct_name = if multiple_operation {
152+
Ident::new(
153+
format!("{}ResponseData", operation.name).as_str(),
154+
Span::call_site(),
155+
)
156+
} else {
157+
Ident::new("ResponseData", Span::call_site())
158+
};
159+
144160
Ok(quote! {
145161
use serde_derive::*;
146162

@@ -166,7 +182,7 @@ pub fn response_for_query(
166182
#variables_struct
167183

168184
#response_derives
169-
pub struct ResponseData {
185+
pub struct #respons_data_struct_name {
170186
#(#response_data_fields,)*
171187
}
172188

graphql_client_codegen/src/deprecation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub enum DeprecationStatus {
1414
}
1515

1616
/// The available deprecation startegies.
17-
#[derive(Debug, PartialEq)]
17+
#[derive(Debug, PartialEq, Clone)]
1818
pub enum DeprecationStrategy {
1919
/// Allow use of deprecated items in queries, and say nothing.
2020
Allow,

0 commit comments

Comments
 (0)