From bd37332733e7797594b89465fa0f742275998edc Mon Sep 17 00:00:00 2001 From: Domenico Visconti Date: Sun, 27 Jun 2021 11:21:46 +0200 Subject: [PATCH 1/4] Add new option fragments-other-variant --- graphql_client_cli/README.md | 2 + graphql_client_cli/src/generate.rs | 4 + graphql_client_cli/src/main.rs | 9 ++- .../src/codegen/selection.rs | 21 ++++- graphql_client_codegen/src/codegen_options.rs | 13 +++ .../src/tests/foobars_query.graphql | 16 ++++ .../src/tests/foobars_schema.graphql | 28 +++++++ graphql_client_codegen/src/tests/mod.rs | 81 ++++++++++++++++++- graphql_query_derive/src/attributes.rs | 68 ++++++++++++++++ graphql_query_derive/src/lib.rs | 2 + 10 files changed, 240 insertions(+), 4 deletions(-) create mode 100644 graphql_client_codegen/src/tests/foobars_query.graphql create mode 100644 graphql_client_codegen/src/tests/foobars_schema.graphql diff --git a/graphql_client_cli/README.md b/graphql_client_cli/README.md index 370772d7a..e23777465 100644 --- a/graphql_client_cli/README.md +++ b/graphql_client_cli/README.md @@ -57,6 +57,8 @@ OPTIONS: -s, --schema-path Path to GraphQL schema file (.json or .graphql). -o, --selected-operation Name of target query. If you don't set this parameter, cli generate all queries in query file. + --fragments-other-variant + Generate an Unknow variant for enums generated by fragments. ARGS: diff --git a/graphql_client_cli/src/generate.rs b/graphql_client_cli/src/generate.rs index b767ba020..1b7aa3709 100644 --- a/graphql_client_cli/src/generate.rs +++ b/graphql_client_cli/src/generate.rs @@ -21,6 +21,7 @@ pub(crate) struct CliCodegenParams { pub module_visibility: Option, pub output_directory: Option, pub custom_scalars_module: Option, + pub fragments_other_variant: bool, } pub(crate) fn generate_code(params: CliCodegenParams) -> CliResult<()> { @@ -35,6 +36,7 @@ pub(crate) fn generate_code(params: CliCodegenParams) -> CliResult<()> { schema_path, selected_operation, custom_scalars_module, + fragments_other_variant, } = params; let deprecation_strategy = deprecation_strategy.as_ref().and_then(|s| s.parse().ok()); @@ -48,6 +50,8 @@ pub(crate) fn generate_code(params: CliCodegenParams) -> CliResult<()> { .into(), ); + options.set_fragments_other_variant(fragments_other_variant); + if let Some(selected_operation) = selected_operation { options.set_operation_name(selected_operation); } diff --git a/graphql_client_cli/src/main.rs b/graphql_client_cli/src/main.rs index 82d9dc1df..1c3a6cfb2 100644 --- a/graphql_client_cli/src/main.rs +++ b/graphql_client_cli/src/main.rs @@ -9,6 +9,7 @@ use error::Error; use log::Level; use std::path::PathBuf; use structopt::StructOpt; +use Cli::Generate; type CliResult = Result; @@ -76,6 +77,10 @@ enum Cli { /// --custom-scalars-module='crate::gql::custom_scalars' #[structopt(short = "p", long = "custom-scalars-module")] custom_scalars_module: Option, + /// A flag indicating if the enum representing the variants of a fragment union/interface should have a "other" variant + /// --fragments-other-variant + #[structopt(long = "fragments-other-variant")] + fragments_other_variant: bool, }, } @@ -97,7 +102,7 @@ fn main() -> CliResult<()> { headers, no_ssl, ), - Cli::Generate { + Generate { variables_derives, response_derives, deprecation_strategy, @@ -108,6 +113,7 @@ fn main() -> CliResult<()> { schema_path, selected_operation, custom_scalars_module, + fragments_other_variant, } => generate::generate_code(generate::CliCodegenParams { query_path, schema_path, @@ -119,6 +125,7 @@ fn main() -> CliResult<()> { module_visibility, output_directory, custom_scalars_module, + fragments_other_variant, }), } } diff --git a/graphql_client_codegen/src/codegen/selection.rs b/graphql_client_codegen/src/codegen/selection.rs index 8e96b31c1..7e5b31161 100644 --- a/graphql_client_codegen/src/codegen/selection.rs +++ b/graphql_client_codegen/src/codegen/selection.rs @@ -197,6 +197,7 @@ fn calculate_selection<'a>( name: variant_name_str.into(), variant_type: Some(variant_struct_name_str.clone().into()), on: struct_id, + is_default_variant: false, }); let expanded_type = ExpandedType { @@ -247,9 +248,19 @@ fn calculate_selection<'a>( name: variant_name_str.into(), on: struct_id, variant_type: None, + is_default_variant: false, }); } } + + if *options.fragments_other_variant() { + context.push_variant(ExpandedVariant { + name: "Unknown".into(), + on: struct_id, + variant_type: None, + is_default_variant: true, + }); + } } } @@ -430,6 +441,7 @@ struct ExpandedVariant<'a> { name: Cow<'a, str>, variant_type: Option>, on: ResponseTypeId, + is_default_variant: bool, } impl<'a> ExpandedVariant<'a> { @@ -440,7 +452,14 @@ impl<'a> ExpandedVariant<'a> { quote!((#ident)) }); - quote!(#name_ident #optional_type_ident) + if self.is_default_variant { + quote! { + #[serde(other)] + #name_ident #optional_type_ident + } + } else { + quote!(#name_ident #optional_type_ident) + } } } diff --git a/graphql_client_codegen/src/codegen_options.rs b/graphql_client_codegen/src/codegen_options.rs index d50ac5b82..634088f7e 100644 --- a/graphql_client_codegen/src/codegen_options.rs +++ b/graphql_client_codegen/src/codegen_options.rs @@ -43,6 +43,8 @@ pub struct GraphQLClientCodegenOptions { custom_scalars_module: Option, /// List of externally defined enum types. Type names must match those used in the schema exactly. extern_enums: Vec, + /// Flag to trigger generation of Other variant for fragments Enum + fragments_other_variant: bool, } impl GraphQLClientCodegenOptions { @@ -62,6 +64,7 @@ impl GraphQLClientCodegenOptions { normalization: Normalization::None, custom_scalars_module: Default::default(), extern_enums: Default::default(), + fragments_other_variant: Default::default(), } } @@ -200,4 +203,14 @@ impl GraphQLClientCodegenOptions { pub fn set_extern_enums(&mut self, enums: Vec) { self.extern_enums = enums; } + + /// Set the graphql client codegen options's fragments other variant. + pub fn set_fragments_other_variant(&mut self, fragments_other_variant: bool) { + self.fragments_other_variant = fragments_other_variant; + } + + /// Get a reference to the graphql client codegen options's fragments other variant. + pub fn fragments_other_variant(&self) -> &bool { + &self.fragments_other_variant + } } diff --git a/graphql_client_codegen/src/tests/foobars_query.graphql b/graphql_client_codegen/src/tests/foobars_query.graphql new file mode 100644 index 000000000..6ac6c7971 --- /dev/null +++ b/graphql_client_codegen/src/tests/foobars_query.graphql @@ -0,0 +1,16 @@ +query FooBarsQuery { + fooBars { + fooBars { + __typename + ... on Foo { + fooField + } + ... on Bar { + barField + } + ... on FooBar { + fooBarField + } + } + } +} diff --git a/graphql_client_codegen/src/tests/foobars_schema.graphql b/graphql_client_codegen/src/tests/foobars_schema.graphql new file mode 100644 index 000000000..cfb19336b --- /dev/null +++ b/graphql_client_codegen/src/tests/foobars_schema.graphql @@ -0,0 +1,28 @@ +schema { + query: Query + mutation: Mutation +} + +directive @defer on FIELD + +type Query { + fooBars: Self +} + +type Self { + fooBars: Result +} + +union Result = Foo | Bar | FooBar + +type Foo { + fooField: String! +} + +type Bar { + barField: String! +} + +type FooBar { + fooBarField: String! +} diff --git a/graphql_client_codegen/src/tests/mod.rs b/graphql_client_codegen/src/tests/mod.rs index 1e24cfbb4..d72172957 100644 --- a/graphql_client_codegen/src/tests/mod.rs +++ b/graphql_client_codegen/src/tests/mod.rs @@ -1,7 +1,7 @@ +use crate::{generated_module, schema::Schema, CodegenMode, GraphQLClientCodegenOptions}; + #[test] fn schema_with_keywords_works() { - use crate::{generated_module, schema::Schema, CodegenMode, GraphQLClientCodegenOptions}; - let query_string = include_str!("keywords_query.graphql"); let query = graphql_parser::parse_query(query_string).expect("Parse keywords query"); let schema = graphql_parser::parse_schema(include_str!("keywords_schema.graphql")) @@ -37,3 +37,80 @@ fn schema_with_keywords_works() { }; } } + +#[test] +fn fragments_other_variant_should_generate_unknown_other_variant() { + let query_string = include_str!("foobars_query.graphql"); + let query = graphql_parser::parse_query(query_string).expect("Parse foobars query"); + let schema = graphql_parser::parse_schema(include_str!("foobars_schema.graphql")) + .expect("Parse foobars schema"); + let schema = Schema::from(schema); + + let mut options = GraphQLClientCodegenOptions::new(CodegenMode::Cli); + + options.set_fragments_other_variant(true); + let query = crate::query::resolve(&schema, &query).unwrap(); + + for (_id, operation) in query.operations() { + let generated_tokens = generated_module::GeneratedModule { + query_string, + schema: &schema, + operation: &operation.name, + resolved_query: &query, + options: &options, + } + .to_token_stream() + .expect("Generate foobars module"); + let generated_code = generated_tokens.to_string(); + + let r: syn::parse::Result = syn::parse2(generated_tokens); + match r { + Ok(_) => { + // Rust keywords should be escaped / renamed now + assert!(generated_code.contains("# [serde (other)] Unknown")); + assert!(generated_code.contains("Unknown")); + } + Err(e) => { + panic!("Error: {}\n Generated content: {}\n", e, &generated_code); + } + }; + } +} + +#[test] +fn fragments_other_variant_false_should_not_generate_unknown_other_variant() { + let query_string = include_str!("foobars_query.graphql"); + let query = graphql_parser::parse_query(query_string).expect("Parse foobars query"); + let schema = graphql_parser::parse_schema(include_str!("foobars_schema.graphql")) + .expect("Parse foobars schema"); + let schema = Schema::from(schema); + + let options = GraphQLClientCodegenOptions::new(CodegenMode::Cli); + + let query = crate::query::resolve(&schema, &query).unwrap(); + + for (_id, operation) in query.operations() { + let generated_tokens = generated_module::GeneratedModule { + query_string, + schema: &schema, + operation: &operation.name, + resolved_query: &query, + options: &options, + } + .to_token_stream() + .expect("Generate foobars module"); + let generated_code = generated_tokens.to_string(); + + let r: syn::parse::Result = syn::parse2(generated_tokens); + match r { + Ok(_) => { + // Rust keywords should be escaped / renamed now + assert!(!generated_code.contains("# [serde (other)] Unknown")); + assert!(!generated_code.contains("Unknown")); + } + Err(e) => { + panic!("Error: {}\n Generated content: {}\n", e, &generated_code); + } + }; + } +} diff --git a/graphql_query_derive/src/attributes.rs b/graphql_query_derive/src/attributes.rs index ff6ccb3f1..0605c97a4 100644 --- a/graphql_query_derive/src/attributes.rs +++ b/graphql_query_derive/src/attributes.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use graphql_client_codegen::deprecation::DeprecationStrategy; use graphql_client_codegen::normalization::Normalization; @@ -94,6 +96,13 @@ pub fn extract_normalization(ast: &syn::DeriveInput) -> Result bool { + extract_attr(&ast, "fragments_other_variant") + .ok() + .and_then(|s| FromStr::from_str(s.as_str()).ok()) + .unwrap_or(false) +} + #[cfg(test)] mod test { use super::*; @@ -151,4 +160,63 @@ mod test { Err(e) => assert_eq!(&format!("{}", e), DEPRECATION_ERROR), }; } + + #[test] + fn test_fragments_other_variant_set_to_true() { + let input = " + #[derive(GraphQLQuery)] + #[graphql( + schema_path = \"x\", + query_path = \"x\", + fragments_other_variant = \"true\", + )] + struct MyQuery; + "; + let parsed = syn::parse_str(input).unwrap(); + assert_eq!(extract_fragments_other_variant(&parsed), true); + } + + #[test] + fn test_fragments_other_variant_set_to_false() { + let input = " + #[derive(GraphQLQuery)] + #[graphql( + schema_path = \"x\", + query_path = \"x\", + fragments_other_variant = \"false\", + )] + struct MyQuery; + "; + let parsed = syn::parse_str(input).unwrap(); + assert_eq!(extract_fragments_other_variant(&parsed), false); + } + + #[test] + fn test_fragments_other_variant_set_to_invalid() { + let input = " + #[derive(GraphQLQuery)] + #[graphql( + schema_path = \"x\", + query_path = \"x\", + fragments_other_variant = \"invalid\", + )] + struct MyQuery; + "; + let parsed = syn::parse_str(input).unwrap(); + assert_eq!(extract_fragments_other_variant(&parsed), false); + } + + #[test] + fn test_fragments_other_variant_unset() { + let input = " + #[derive(GraphQLQuery)] + #[graphql( + schema_path = \"x\", + query_path = \"x\", + )] + struct MyQuery; + "; + let parsed = syn::parse_str(input).unwrap(); + assert_eq!(extract_fragments_other_variant(&parsed), false); + } } diff --git a/graphql_query_derive/src/lib.rs b/graphql_query_derive/src/lib.rs index 56de45bf1..360ad0f3e 100644 --- a/graphql_query_derive/src/lib.rs +++ b/graphql_query_derive/src/lib.rs @@ -63,9 +63,11 @@ fn build_graphql_client_derive_options( let response_derives = attributes::extract_attr(input, "response_derives").ok(); let custom_scalars_module = attributes::extract_attr(input, "custom_scalars_module").ok(); let extern_enums = attributes::extract_attr_list(input, "extern_enums").ok(); + let fragments_other_variant: bool = attributes::extract_fragments_other_variant(input); let mut options = GraphQLClientCodegenOptions::new(CodegenMode::Derive); options.set_query_file(query_path); + options.set_fragments_other_variant(fragments_other_variant); if let Some(variables_derives) = variables_derives { options.set_variables_derives(variables_derives); From 54aa4975888979f69b414fe9acfd0932280b76a2 Mon Sep 17 00:00:00 2001 From: Domenico Visconti Date: Wed, 30 Jun 2021 15:12:28 +0200 Subject: [PATCH 2/4] create Unknow variant for fragment as default behaviour --- graphql_client_cli/README.md | 3 - graphql_client_cli/src/generate.rs | 4 -- graphql_client_cli/src/main.rs | 6 -- .../src/codegen/selection.rs | 35 +++------- graphql_client_codegen/src/codegen_options.rs | 13 ---- graphql_client_codegen/src/tests/mod.rs | 42 +----------- graphql_query_derive/src/attributes.rs | 68 ------------------- graphql_query_derive/src/lib.rs | 2 - 8 files changed, 10 insertions(+), 163 deletions(-) diff --git a/graphql_client_cli/README.md b/graphql_client_cli/README.md index e23777465..8290a7a73 100644 --- a/graphql_client_cli/README.md +++ b/graphql_client_cli/README.md @@ -57,9 +57,6 @@ OPTIONS: -s, --schema-path Path to GraphQL schema file (.json or .graphql). -o, --selected-operation Name of target query. If you don't set this parameter, cli generate all queries in query file. - --fragments-other-variant - Generate an Unknow variant for enums generated by fragments. - ARGS: Path to the GraphQL query file. diff --git a/graphql_client_cli/src/generate.rs b/graphql_client_cli/src/generate.rs index 1b7aa3709..b767ba020 100644 --- a/graphql_client_cli/src/generate.rs +++ b/graphql_client_cli/src/generate.rs @@ -21,7 +21,6 @@ pub(crate) struct CliCodegenParams { pub module_visibility: Option, pub output_directory: Option, pub custom_scalars_module: Option, - pub fragments_other_variant: bool, } pub(crate) fn generate_code(params: CliCodegenParams) -> CliResult<()> { @@ -36,7 +35,6 @@ pub(crate) fn generate_code(params: CliCodegenParams) -> CliResult<()> { schema_path, selected_operation, custom_scalars_module, - fragments_other_variant, } = params; let deprecation_strategy = deprecation_strategy.as_ref().and_then(|s| s.parse().ok()); @@ -50,8 +48,6 @@ pub(crate) fn generate_code(params: CliCodegenParams) -> CliResult<()> { .into(), ); - options.set_fragments_other_variant(fragments_other_variant); - if let Some(selected_operation) = selected_operation { options.set_operation_name(selected_operation); } diff --git a/graphql_client_cli/src/main.rs b/graphql_client_cli/src/main.rs index 1c3a6cfb2..400e6cf8c 100644 --- a/graphql_client_cli/src/main.rs +++ b/graphql_client_cli/src/main.rs @@ -77,10 +77,6 @@ enum Cli { /// --custom-scalars-module='crate::gql::custom_scalars' #[structopt(short = "p", long = "custom-scalars-module")] custom_scalars_module: Option, - /// A flag indicating if the enum representing the variants of a fragment union/interface should have a "other" variant - /// --fragments-other-variant - #[structopt(long = "fragments-other-variant")] - fragments_other_variant: bool, }, } @@ -113,7 +109,6 @@ fn main() -> CliResult<()> { schema_path, selected_operation, custom_scalars_module, - fragments_other_variant, } => generate::generate_code(generate::CliCodegenParams { query_path, schema_path, @@ -125,7 +120,6 @@ fn main() -> CliResult<()> { module_visibility, output_directory, custom_scalars_module, - fragments_other_variant, }), } } diff --git a/graphql_client_codegen/src/codegen/selection.rs b/graphql_client_codegen/src/codegen/selection.rs index 7e5b31161..ef39ded2e 100644 --- a/graphql_client_codegen/src/codegen/selection.rs +++ b/graphql_client_codegen/src/codegen/selection.rs @@ -197,7 +197,6 @@ fn calculate_selection<'a>( name: variant_name_str.into(), variant_type: Some(variant_struct_name_str.clone().into()), on: struct_id, - is_default_variant: false, }); let expanded_type = ExpandedType { @@ -248,19 +247,9 @@ fn calculate_selection<'a>( name: variant_name_str.into(), on: struct_id, variant_type: None, - is_default_variant: false, }); } } - - if *options.fragments_other_variant() { - context.push_variant(ExpandedVariant { - name: "Unknown".into(), - on: struct_id, - variant_type: None, - is_default_variant: true, - }); - } } } @@ -441,7 +430,6 @@ struct ExpandedVariant<'a> { name: Cow<'a, str>, variant_type: Option>, on: ResponseTypeId, - is_default_variant: bool, } impl<'a> ExpandedVariant<'a> { @@ -452,14 +440,7 @@ impl<'a> ExpandedVariant<'a> { quote!((#ident)) }); - if self.is_default_variant { - quote! { - #[serde(other)] - #name_ident #optional_type_ident - } - } else { - quote!(#name_ident #optional_type_ident) - } + quote!(#name_ident #optional_type_ident) } } @@ -557,12 +538,14 @@ impl<'a> ExpandedSelection<'a> { // of the variants. if fields.peek().is_none() { let item = quote! { - #response_derives - #[serde(tag = "__typename")] - pub enum #struct_name { - #(#on_variants),* - } - }; + #response_derives + #[serde(tag = "__typename")] + pub enum #struct_name { + #(#on_variants,)* + #[serde(other)] + Unknown, + } + }; items.push(item); continue; } diff --git a/graphql_client_codegen/src/codegen_options.rs b/graphql_client_codegen/src/codegen_options.rs index 634088f7e..d50ac5b82 100644 --- a/graphql_client_codegen/src/codegen_options.rs +++ b/graphql_client_codegen/src/codegen_options.rs @@ -43,8 +43,6 @@ pub struct GraphQLClientCodegenOptions { custom_scalars_module: Option, /// List of externally defined enum types. Type names must match those used in the schema exactly. extern_enums: Vec, - /// Flag to trigger generation of Other variant for fragments Enum - fragments_other_variant: bool, } impl GraphQLClientCodegenOptions { @@ -64,7 +62,6 @@ impl GraphQLClientCodegenOptions { normalization: Normalization::None, custom_scalars_module: Default::default(), extern_enums: Default::default(), - fragments_other_variant: Default::default(), } } @@ -203,14 +200,4 @@ impl GraphQLClientCodegenOptions { pub fn set_extern_enums(&mut self, enums: Vec) { self.extern_enums = enums; } - - /// Set the graphql client codegen options's fragments other variant. - pub fn set_fragments_other_variant(&mut self, fragments_other_variant: bool) { - self.fragments_other_variant = fragments_other_variant; - } - - /// Get a reference to the graphql client codegen options's fragments other variant. - pub fn fragments_other_variant(&self) -> &bool { - &self.fragments_other_variant - } } diff --git a/graphql_client_codegen/src/tests/mod.rs b/graphql_client_codegen/src/tests/mod.rs index d72172957..2b073f8ef 100644 --- a/graphql_client_codegen/src/tests/mod.rs +++ b/graphql_client_codegen/src/tests/mod.rs @@ -46,45 +46,6 @@ fn fragments_other_variant_should_generate_unknown_other_variant() { .expect("Parse foobars schema"); let schema = Schema::from(schema); - let mut options = GraphQLClientCodegenOptions::new(CodegenMode::Cli); - - options.set_fragments_other_variant(true); - let query = crate::query::resolve(&schema, &query).unwrap(); - - for (_id, operation) in query.operations() { - let generated_tokens = generated_module::GeneratedModule { - query_string, - schema: &schema, - operation: &operation.name, - resolved_query: &query, - options: &options, - } - .to_token_stream() - .expect("Generate foobars module"); - let generated_code = generated_tokens.to_string(); - - let r: syn::parse::Result = syn::parse2(generated_tokens); - match r { - Ok(_) => { - // Rust keywords should be escaped / renamed now - assert!(generated_code.contains("# [serde (other)] Unknown")); - assert!(generated_code.contains("Unknown")); - } - Err(e) => { - panic!("Error: {}\n Generated content: {}\n", e, &generated_code); - } - }; - } -} - -#[test] -fn fragments_other_variant_false_should_not_generate_unknown_other_variant() { - let query_string = include_str!("foobars_query.graphql"); - let query = graphql_parser::parse_query(query_string).expect("Parse foobars query"); - let schema = graphql_parser::parse_schema(include_str!("foobars_schema.graphql")) - .expect("Parse foobars schema"); - let schema = Schema::from(schema); - let options = GraphQLClientCodegenOptions::new(CodegenMode::Cli); let query = crate::query::resolve(&schema, &query).unwrap(); @@ -105,8 +66,7 @@ fn fragments_other_variant_false_should_not_generate_unknown_other_variant() { match r { Ok(_) => { // Rust keywords should be escaped / renamed now - assert!(!generated_code.contains("# [serde (other)] Unknown")); - assert!(!generated_code.contains("Unknown")); + assert!(generated_code.contains("# [serde (other)] Unknown")); } Err(e) => { panic!("Error: {}\n Generated content: {}\n", e, &generated_code); diff --git a/graphql_query_derive/src/attributes.rs b/graphql_query_derive/src/attributes.rs index 0605c97a4..ff6ccb3f1 100644 --- a/graphql_query_derive/src/attributes.rs +++ b/graphql_query_derive/src/attributes.rs @@ -1,5 +1,3 @@ -use std::str::FromStr; - use graphql_client_codegen::deprecation::DeprecationStrategy; use graphql_client_codegen::normalization::Normalization; @@ -96,13 +94,6 @@ pub fn extract_normalization(ast: &syn::DeriveInput) -> Result bool { - extract_attr(&ast, "fragments_other_variant") - .ok() - .and_then(|s| FromStr::from_str(s.as_str()).ok()) - .unwrap_or(false) -} - #[cfg(test)] mod test { use super::*; @@ -160,63 +151,4 @@ mod test { Err(e) => assert_eq!(&format!("{}", e), DEPRECATION_ERROR), }; } - - #[test] - fn test_fragments_other_variant_set_to_true() { - let input = " - #[derive(GraphQLQuery)] - #[graphql( - schema_path = \"x\", - query_path = \"x\", - fragments_other_variant = \"true\", - )] - struct MyQuery; - "; - let parsed = syn::parse_str(input).unwrap(); - assert_eq!(extract_fragments_other_variant(&parsed), true); - } - - #[test] - fn test_fragments_other_variant_set_to_false() { - let input = " - #[derive(GraphQLQuery)] - #[graphql( - schema_path = \"x\", - query_path = \"x\", - fragments_other_variant = \"false\", - )] - struct MyQuery; - "; - let parsed = syn::parse_str(input).unwrap(); - assert_eq!(extract_fragments_other_variant(&parsed), false); - } - - #[test] - fn test_fragments_other_variant_set_to_invalid() { - let input = " - #[derive(GraphQLQuery)] - #[graphql( - schema_path = \"x\", - query_path = \"x\", - fragments_other_variant = \"invalid\", - )] - struct MyQuery; - "; - let parsed = syn::parse_str(input).unwrap(); - assert_eq!(extract_fragments_other_variant(&parsed), false); - } - - #[test] - fn test_fragments_other_variant_unset() { - let input = " - #[derive(GraphQLQuery)] - #[graphql( - schema_path = \"x\", - query_path = \"x\", - )] - struct MyQuery; - "; - let parsed = syn::parse_str(input).unwrap(); - assert_eq!(extract_fragments_other_variant(&parsed), false); - } } diff --git a/graphql_query_derive/src/lib.rs b/graphql_query_derive/src/lib.rs index 360ad0f3e..56de45bf1 100644 --- a/graphql_query_derive/src/lib.rs +++ b/graphql_query_derive/src/lib.rs @@ -63,11 +63,9 @@ fn build_graphql_client_derive_options( let response_derives = attributes::extract_attr(input, "response_derives").ok(); let custom_scalars_module = attributes::extract_attr(input, "custom_scalars_module").ok(); let extern_enums = attributes::extract_attr_list(input, "extern_enums").ok(); - let fragments_other_variant: bool = attributes::extract_fragments_other_variant(input); let mut options = GraphQLClientCodegenOptions::new(CodegenMode::Derive); options.set_query_file(query_path); - options.set_fragments_other_variant(fragments_other_variant); if let Some(variables_derives) = variables_derives { options.set_variables_derives(variables_derives); From 9bc7f50506c108707778883de9b82ade2c3fa433 Mon Sep 17 00:00:00 2001 From: Domenico Visconti Date: Sun, 4 Jul 2021 17:43:11 +0200 Subject: [PATCH 3/4] Revert "create Unknow variant for fragment as default behaviour" This reverts commit 20c8e6dd875f1d53868cd181285499c3e6637682. --- graphql_client_cli/README.md | 3 + graphql_client_cli/src/generate.rs | 4 ++ graphql_client_cli/src/main.rs | 6 ++ .../src/codegen/selection.rs | 35 +++++++--- graphql_client_codegen/src/codegen_options.rs | 13 ++++ graphql_client_codegen/src/tests/mod.rs | 42 +++++++++++- graphql_query_derive/src/attributes.rs | 68 +++++++++++++++++++ graphql_query_derive/src/lib.rs | 2 + 8 files changed, 163 insertions(+), 10 deletions(-) diff --git a/graphql_client_cli/README.md b/graphql_client_cli/README.md index 8290a7a73..e23777465 100644 --- a/graphql_client_cli/README.md +++ b/graphql_client_cli/README.md @@ -57,6 +57,9 @@ OPTIONS: -s, --schema-path Path to GraphQL schema file (.json or .graphql). -o, --selected-operation Name of target query. If you don't set this parameter, cli generate all queries in query file. + --fragments-other-variant + Generate an Unknow variant for enums generated by fragments. + ARGS: Path to the GraphQL query file. diff --git a/graphql_client_cli/src/generate.rs b/graphql_client_cli/src/generate.rs index b767ba020..1b7aa3709 100644 --- a/graphql_client_cli/src/generate.rs +++ b/graphql_client_cli/src/generate.rs @@ -21,6 +21,7 @@ pub(crate) struct CliCodegenParams { pub module_visibility: Option, pub output_directory: Option, pub custom_scalars_module: Option, + pub fragments_other_variant: bool, } pub(crate) fn generate_code(params: CliCodegenParams) -> CliResult<()> { @@ -35,6 +36,7 @@ pub(crate) fn generate_code(params: CliCodegenParams) -> CliResult<()> { schema_path, selected_operation, custom_scalars_module, + fragments_other_variant, } = params; let deprecation_strategy = deprecation_strategy.as_ref().and_then(|s| s.parse().ok()); @@ -48,6 +50,8 @@ pub(crate) fn generate_code(params: CliCodegenParams) -> CliResult<()> { .into(), ); + options.set_fragments_other_variant(fragments_other_variant); + if let Some(selected_operation) = selected_operation { options.set_operation_name(selected_operation); } diff --git a/graphql_client_cli/src/main.rs b/graphql_client_cli/src/main.rs index 400e6cf8c..1c3a6cfb2 100644 --- a/graphql_client_cli/src/main.rs +++ b/graphql_client_cli/src/main.rs @@ -77,6 +77,10 @@ enum Cli { /// --custom-scalars-module='crate::gql::custom_scalars' #[structopt(short = "p", long = "custom-scalars-module")] custom_scalars_module: Option, + /// A flag indicating if the enum representing the variants of a fragment union/interface should have a "other" variant + /// --fragments-other-variant + #[structopt(long = "fragments-other-variant")] + fragments_other_variant: bool, }, } @@ -109,6 +113,7 @@ fn main() -> CliResult<()> { schema_path, selected_operation, custom_scalars_module, + fragments_other_variant, } => generate::generate_code(generate::CliCodegenParams { query_path, schema_path, @@ -120,6 +125,7 @@ fn main() -> CliResult<()> { module_visibility, output_directory, custom_scalars_module, + fragments_other_variant, }), } } diff --git a/graphql_client_codegen/src/codegen/selection.rs b/graphql_client_codegen/src/codegen/selection.rs index ef39ded2e..7e5b31161 100644 --- a/graphql_client_codegen/src/codegen/selection.rs +++ b/graphql_client_codegen/src/codegen/selection.rs @@ -197,6 +197,7 @@ fn calculate_selection<'a>( name: variant_name_str.into(), variant_type: Some(variant_struct_name_str.clone().into()), on: struct_id, + is_default_variant: false, }); let expanded_type = ExpandedType { @@ -247,9 +248,19 @@ fn calculate_selection<'a>( name: variant_name_str.into(), on: struct_id, variant_type: None, + is_default_variant: false, }); } } + + if *options.fragments_other_variant() { + context.push_variant(ExpandedVariant { + name: "Unknown".into(), + on: struct_id, + variant_type: None, + is_default_variant: true, + }); + } } } @@ -430,6 +441,7 @@ struct ExpandedVariant<'a> { name: Cow<'a, str>, variant_type: Option>, on: ResponseTypeId, + is_default_variant: bool, } impl<'a> ExpandedVariant<'a> { @@ -440,7 +452,14 @@ impl<'a> ExpandedVariant<'a> { quote!((#ident)) }); - quote!(#name_ident #optional_type_ident) + if self.is_default_variant { + quote! { + #[serde(other)] + #name_ident #optional_type_ident + } + } else { + quote!(#name_ident #optional_type_ident) + } } } @@ -538,14 +557,12 @@ impl<'a> ExpandedSelection<'a> { // of the variants. if fields.peek().is_none() { let item = quote! { - #response_derives - #[serde(tag = "__typename")] - pub enum #struct_name { - #(#on_variants,)* - #[serde(other)] - Unknown, - } - }; + #response_derives + #[serde(tag = "__typename")] + pub enum #struct_name { + #(#on_variants),* + } + }; items.push(item); continue; } diff --git a/graphql_client_codegen/src/codegen_options.rs b/graphql_client_codegen/src/codegen_options.rs index d50ac5b82..634088f7e 100644 --- a/graphql_client_codegen/src/codegen_options.rs +++ b/graphql_client_codegen/src/codegen_options.rs @@ -43,6 +43,8 @@ pub struct GraphQLClientCodegenOptions { custom_scalars_module: Option, /// List of externally defined enum types. Type names must match those used in the schema exactly. extern_enums: Vec, + /// Flag to trigger generation of Other variant for fragments Enum + fragments_other_variant: bool, } impl GraphQLClientCodegenOptions { @@ -62,6 +64,7 @@ impl GraphQLClientCodegenOptions { normalization: Normalization::None, custom_scalars_module: Default::default(), extern_enums: Default::default(), + fragments_other_variant: Default::default(), } } @@ -200,4 +203,14 @@ impl GraphQLClientCodegenOptions { pub fn set_extern_enums(&mut self, enums: Vec) { self.extern_enums = enums; } + + /// Set the graphql client codegen options's fragments other variant. + pub fn set_fragments_other_variant(&mut self, fragments_other_variant: bool) { + self.fragments_other_variant = fragments_other_variant; + } + + /// Get a reference to the graphql client codegen options's fragments other variant. + pub fn fragments_other_variant(&self) -> &bool { + &self.fragments_other_variant + } } diff --git a/graphql_client_codegen/src/tests/mod.rs b/graphql_client_codegen/src/tests/mod.rs index 2b073f8ef..d72172957 100644 --- a/graphql_client_codegen/src/tests/mod.rs +++ b/graphql_client_codegen/src/tests/mod.rs @@ -46,8 +46,9 @@ fn fragments_other_variant_should_generate_unknown_other_variant() { .expect("Parse foobars schema"); let schema = Schema::from(schema); - let options = GraphQLClientCodegenOptions::new(CodegenMode::Cli); + let mut options = GraphQLClientCodegenOptions::new(CodegenMode::Cli); + options.set_fragments_other_variant(true); let query = crate::query::resolve(&schema, &query).unwrap(); for (_id, operation) in query.operations() { @@ -67,6 +68,45 @@ fn fragments_other_variant_should_generate_unknown_other_variant() { Ok(_) => { // Rust keywords should be escaped / renamed now assert!(generated_code.contains("# [serde (other)] Unknown")); + assert!(generated_code.contains("Unknown")); + } + Err(e) => { + panic!("Error: {}\n Generated content: {}\n", e, &generated_code); + } + }; + } +} + +#[test] +fn fragments_other_variant_false_should_not_generate_unknown_other_variant() { + let query_string = include_str!("foobars_query.graphql"); + let query = graphql_parser::parse_query(query_string).expect("Parse foobars query"); + let schema = graphql_parser::parse_schema(include_str!("foobars_schema.graphql")) + .expect("Parse foobars schema"); + let schema = Schema::from(schema); + + let options = GraphQLClientCodegenOptions::new(CodegenMode::Cli); + + let query = crate::query::resolve(&schema, &query).unwrap(); + + for (_id, operation) in query.operations() { + let generated_tokens = generated_module::GeneratedModule { + query_string, + schema: &schema, + operation: &operation.name, + resolved_query: &query, + options: &options, + } + .to_token_stream() + .expect("Generate foobars module"); + let generated_code = generated_tokens.to_string(); + + let r: syn::parse::Result = syn::parse2(generated_tokens); + match r { + Ok(_) => { + // Rust keywords should be escaped / renamed now + assert!(!generated_code.contains("# [serde (other)] Unknown")); + assert!(!generated_code.contains("Unknown")); } Err(e) => { panic!("Error: {}\n Generated content: {}\n", e, &generated_code); diff --git a/graphql_query_derive/src/attributes.rs b/graphql_query_derive/src/attributes.rs index ff6ccb3f1..0605c97a4 100644 --- a/graphql_query_derive/src/attributes.rs +++ b/graphql_query_derive/src/attributes.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use graphql_client_codegen::deprecation::DeprecationStrategy; use graphql_client_codegen::normalization::Normalization; @@ -94,6 +96,13 @@ pub fn extract_normalization(ast: &syn::DeriveInput) -> Result bool { + extract_attr(&ast, "fragments_other_variant") + .ok() + .and_then(|s| FromStr::from_str(s.as_str()).ok()) + .unwrap_or(false) +} + #[cfg(test)] mod test { use super::*; @@ -151,4 +160,63 @@ mod test { Err(e) => assert_eq!(&format!("{}", e), DEPRECATION_ERROR), }; } + + #[test] + fn test_fragments_other_variant_set_to_true() { + let input = " + #[derive(GraphQLQuery)] + #[graphql( + schema_path = \"x\", + query_path = \"x\", + fragments_other_variant = \"true\", + )] + struct MyQuery; + "; + let parsed = syn::parse_str(input).unwrap(); + assert_eq!(extract_fragments_other_variant(&parsed), true); + } + + #[test] + fn test_fragments_other_variant_set_to_false() { + let input = " + #[derive(GraphQLQuery)] + #[graphql( + schema_path = \"x\", + query_path = \"x\", + fragments_other_variant = \"false\", + )] + struct MyQuery; + "; + let parsed = syn::parse_str(input).unwrap(); + assert_eq!(extract_fragments_other_variant(&parsed), false); + } + + #[test] + fn test_fragments_other_variant_set_to_invalid() { + let input = " + #[derive(GraphQLQuery)] + #[graphql( + schema_path = \"x\", + query_path = \"x\", + fragments_other_variant = \"invalid\", + )] + struct MyQuery; + "; + let parsed = syn::parse_str(input).unwrap(); + assert_eq!(extract_fragments_other_variant(&parsed), false); + } + + #[test] + fn test_fragments_other_variant_unset() { + let input = " + #[derive(GraphQLQuery)] + #[graphql( + schema_path = \"x\", + query_path = \"x\", + )] + struct MyQuery; + "; + let parsed = syn::parse_str(input).unwrap(); + assert_eq!(extract_fragments_other_variant(&parsed), false); + } } diff --git a/graphql_query_derive/src/lib.rs b/graphql_query_derive/src/lib.rs index 56de45bf1..360ad0f3e 100644 --- a/graphql_query_derive/src/lib.rs +++ b/graphql_query_derive/src/lib.rs @@ -63,9 +63,11 @@ fn build_graphql_client_derive_options( let response_derives = attributes::extract_attr(input, "response_derives").ok(); let custom_scalars_module = attributes::extract_attr(input, "custom_scalars_module").ok(); let extern_enums = attributes::extract_attr_list(input, "extern_enums").ok(); + let fragments_other_variant: bool = attributes::extract_fragments_other_variant(input); let mut options = GraphQLClientCodegenOptions::new(CodegenMode::Derive); options.set_query_file(query_path); + options.set_fragments_other_variant(fragments_other_variant); if let Some(variables_derives) = variables_derives { options.set_variables_derives(variables_derives); From 641b1a0bbfac1af149ff66db88a34b491abec80e Mon Sep 17 00:00:00 2001 From: Domenico Visconti Date: Sun, 4 Jul 2021 17:51:43 +0200 Subject: [PATCH 4/4] clippy --- graphql_query_derive/src/attributes.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/graphql_query_derive/src/attributes.rs b/graphql_query_derive/src/attributes.rs index 0605c97a4..547632a1b 100644 --- a/graphql_query_derive/src/attributes.rs +++ b/graphql_query_derive/src/attributes.rs @@ -173,7 +173,7 @@ mod test { struct MyQuery; "; let parsed = syn::parse_str(input).unwrap(); - assert_eq!(extract_fragments_other_variant(&parsed), true); + assert!(extract_fragments_other_variant(&parsed)); } #[test] @@ -188,7 +188,7 @@ mod test { struct MyQuery; "; let parsed = syn::parse_str(input).unwrap(); - assert_eq!(extract_fragments_other_variant(&parsed), false); + assert!(!extract_fragments_other_variant(&parsed)); } #[test] @@ -203,7 +203,7 @@ mod test { struct MyQuery; "; let parsed = syn::parse_str(input).unwrap(); - assert_eq!(extract_fragments_other_variant(&parsed), false); + assert!(!extract_fragments_other_variant(&parsed)); } #[test] @@ -217,6 +217,6 @@ mod test { struct MyQuery; "; let parsed = syn::parse_str(input).unwrap(); - assert_eq!(extract_fragments_other_variant(&parsed), false); + assert!(!extract_fragments_other_variant(&parsed)); } }