Skip to content

Commit

Permalink
Merge pull request #84 from tomhoule/introspection-query-bug
Browse files Browse the repository at this point in the history
Fix issues from #83
  • Loading branch information
tomhoule authored Aug 23, 2018
2 parents 96e2f98 + 93bb6ba commit 883babd
Show file tree
Hide file tree
Showing 14 changed files with 6,232 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ language: rust
rust:
- stable
- beta
- nightly
# - nightly
cache: cargo
before_script:
- if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then (rustup component add rustfmt-preview clippy-preview) fi
Expand Down
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## Unreleased

There are a number of breaking new features, read the `Added` section attentively if you are upgrading.

### Added

- (breaking) Control over which types custom scalars deserialize to is given to the user: you now have to provide type aliases for the custom scalars in the scope of the struct under derive.
- (breaking) Support for multi-operations documents. You can select a particular operation by naming the struct under derive after it. In case there is no match, we revert to the current behaviour: select the first operation.
- Support arbitrary derives on the generated response types via the `response_derives` option on the `graphql` attribute.
- (breaking) Support arbitrary derives on the generated response types via the `response_derives` option on the `graphql` attribute. If you were relying on the `Debug` impl on generated structs before, you need to add `response_derives = "Debug"` in the `#[graphql()]` attributes in your structs.

### Fixed

- Fixed codegen of fields with leading underscores - they were ignored, leading to wrong derived types for deserialization.
- Made the CLI dump introspected schemas directly without trying to validate them.

## [0.3.0] - 2018-07-24

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,12 @@ A typed GraphQL client library for Rust.
extern crate graphql_client;
extern crate reqwest;

use graphql_client::{GraphQLQuery, GraphQLResponse};

fn perform_my_query(variables: &my_query::Variables) -> Result<(), failure::Error> {

// this is the important line
let request_body = MyQuery::expand(variables);
let request_body = MyQuery::build_query(variables);

let client = reqwest::Client::new();
let mut res = client.post("/graphql").json(&request_body).send()?;
Expand Down
10 changes: 3 additions & 7 deletions graphql_client_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use structopt::StructOpt;
#[graphql(
schema_path = "src/introspection_schema.graphql",
query_path = "src/introspection_query.graphql",
response_derives = "Serialize",
response_derives = "Serialize"
)]
struct IntrospectionQuery;

Expand Down Expand Up @@ -86,10 +86,6 @@ fn introspect_schema(location: String, output: Option<PathBuf>) -> Result<(), fa
println!("Something else happened. Status: {:?}", res.status());
}

let json: graphql_client::GraphQLResponse<introspection_query::ResponseData> = res.json()?;
let json = serde_json::to_string(&json)?;

write!(out, "{}", json)?;

Ok(())
let json: serde_json::Value = res.json()?;
Ok(serde_json::to_writer_pretty(out, &json)?)
}
1 change: 0 additions & 1 deletion graphql_query_derive/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ pub(crate) fn response_for_query(
#variables_struct

#response_derives
#[serde(rename_all = "camelCase")]
pub struct ResponseData {
#(#response_data_fields,)*
}
Expand Down
11 changes: 7 additions & 4 deletions graphql_query_derive/src/inputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@ impl GqlInput {
fields.sort_unstable_by(|a, b| a.name.cmp(&b.name));
let fields = fields.iter().map(|field| {
let ty = field.type_.to_rust(&context, "");
let name = Ident::new(&field.name.to_snake_case(), Span::call_site());
quote!(pub #name: #ty)
let original_name = &field.name;
let snake_case_name = field.name.to_snake_case();
let rename = ::shared::field_rename_annotation(&original_name, &snake_case_name);
let name = Ident::new(&snake_case_name, Span::call_site());

quote!(#rename pub #name: #ty)
});

Ok(quote! {
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct #name {
#(#fields,)*
}
Expand Down Expand Up @@ -132,9 +135,9 @@ mod tests {

let expected: String = vec![
"# [ derive ( Debug , Serialize ) ] ",
"# [ serde ( rename_all = \"camelCase\" ) ] ",
"pub struct Cat { ",
"pub offsprings : Vec < Cat > , ",
"# [ serde ( rename = \"pawsCount\" ) ] ",
"pub paws_count : Float , ",
"pub requirements : Option < CatRequirements > , ",
"}",
Expand Down
1 change: 0 additions & 1 deletion graphql_query_derive/src/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ impl GqlObject {
#(#field_impls)*

#derives
#[serde(rename_all = "camelCase")]
#description
pub struct #name {
#(#fields,)*
Expand Down
8 changes: 5 additions & 3 deletions graphql_query_derive/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,11 @@ impl Operation {
let fields = variables.iter().map(|variable| {
let name = &variable.name;
let ty = variable.ty.to_rust(context, "");
let name = Ident::new(&name.to_snake_case(), Span::call_site());
quote!(pub #name: #ty)
let snake_case_name = name.to_snake_case();
let rename = ::shared::field_rename_annotation(&name, &snake_case_name);
let name = Ident::new(&snake_case_name, Span::call_site());

quote!(#rename pub #name: #ty)
});

let default_constructors = variables
Expand All @@ -60,7 +63,6 @@ impl Operation {

quote! {
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Variables {
#(#fields,)*
}
Expand Down
17 changes: 15 additions & 2 deletions graphql_query_derive/src/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ pub(crate) fn render_object_field(
};
}

let name_ident = Ident::new(&field_name.to_snake_case(), Span::call_site());
let snake_case_name = field_name.to_snake_case();
let rename = ::shared::field_rename_annotation(&field_name, &snake_case_name);
let name_ident = Ident::new(&snake_case_name, Span::call_site());

quote!(#description pub #name_ident: #field_type)
quote!(#description #rename pub #name_ident: #field_type)
}

pub(crate) fn field_impls_for_selection(
Expand Down Expand Up @@ -96,3 +98,14 @@ pub(crate) fn response_fields_for_selection(
}
}).collect()
}

/// Given the GraphQL schema name for an object/interface/input object field and
/// the equivalent rust name, produces a serde annotation to map them during
/// (de)serialization if it is necessary, otherwise an empty TokenStream.
pub(crate) fn field_rename_annotation(graphql_name: &str, rust_name: &str) -> TokenStream {
if graphql_name != rust_name {
quote!(#[serde(rename = #graphql_name)])
} else {
quote!()
}
}
24 changes: 11 additions & 13 deletions graphql_query_derive/src/unions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ mod tests {
SelectionItem::InlineFragment(SelectionInlineFragment {
on: "User".to_string(),
fields: Selection(vec![SelectionItem::Field(SelectionField {
name: "first_name".to_string(),
name: "firstName".to_string(),
fields: Selection(vec![]),
})]),
}),
Expand Down Expand Up @@ -178,17 +178,17 @@ mod tests {
fields: vec![
GqlObjectField {
description: None,
name: "first_name".to_string(),
name: "firstName".to_string(),
type_: FieldType::Named(Ident::new("String", Span::call_site())),
},
GqlObjectField {
description: None,
name: "last_name".to_string(),
name: "lastName".to_string(),
type_: FieldType::Named(Ident::new("String", Span::call_site())),
},
GqlObjectField {
description: None,
name: "created_at".to_string(),
name: "createdAt".to_string(),
type_: FieldType::Named(Ident::new("Date", Span::call_site())),
},
],
Expand Down Expand Up @@ -235,7 +235,7 @@ mod tests {
SelectionItem::InlineFragment(SelectionInlineFragment {
on: "User".to_string(),
fields: Selection(vec![SelectionItem::Field(SelectionField {
name: "first_name".to_string(),
name: "firstName".to_string(),
fields: Selection(vec![]),
})]),
}),
Expand Down Expand Up @@ -272,17 +272,17 @@ mod tests {
},
GqlObjectField {
description: None,
name: "first_name".to_string(),
name: "firstName".to_string(),
type_: FieldType::Named(string_type()),
},
GqlObjectField {
description: None,
name: "last_name".to_string(),
name: "lastName".to_string(),
type_: FieldType::Named(string_type()),
},
GqlObjectField {
description: None,
name: "created_at".to_string(),
name: "createdAt".to_string(),
type_: FieldType::Named(Ident::new("Date", Span::call_site())),
},
],
Expand All @@ -307,7 +307,7 @@ mod tests {
},
GqlObjectField {
description: None,
name: "created_at".to_string(),
name: "createdAt".to_string(),
type_: FieldType::Named(Ident::new("Date", Span::call_site())),
},
],
Expand All @@ -324,16 +324,14 @@ mod tests {
result.unwrap().to_string(),
vec![
"# [ derive ( Deserialize ) ] ",
"# [ serde ( rename_all = \"camelCase\" ) ] ",
"pub struct MeowOnUser { pub first_name : String , } ",
"pub struct MeowOnUser { # [ serde ( rename = \"firstName\" ) ] pub first_name : String , } ",
"# [ derive ( Deserialize ) ] ",
"# [ serde ( rename_all = \"camelCase\" ) ] ",
"pub struct MeowOnOrganization { pub title : String , } ",
"# [ derive ( Deserialize ) ] ",
"# [ serde ( tag = \"__typename\" ) ] ",
"pub enum Meow { User ( MeowOnUser ) , Organization ( MeowOnOrganization ) }",
].into_iter()
.collect::<String>(),
.collect::<String>(),
);
}
}
13 changes: 13 additions & 0 deletions tests/introspection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ extern crate graphql_client;
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;

#[derive(GraphQLQuery)]
#[graphql(
query_path = "tests/introspection/introspection_query.graphql",
schema_path = "tests/introspection/introspection_schema.graphql",
response_derives = "Debug,PartialEq"
)]
#[allow(dead_code)]
struct IntrospectionQuery;
Expand All @@ -16,3 +18,14 @@ struct IntrospectionQuery;
fn introspection_schema() {
()
}

const INTROSPECTION_RESPONSE: &'static str =
include_str!("./introspection/introspection_response.json");

#[test]
fn leading_underscores_are_preserved() {
let deserialized: graphql_client::GraphQLResponse<introspection_query::ResponseData> =
serde_json::from_str(INTROSPECTION_RESPONSE).unwrap();
assert!(deserialized.data.is_some());
assert!(deserialized.data.unwrap().schema.is_some());
}
2 changes: 1 addition & 1 deletion tests/introspection/introspection_query.graphql
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
query IntrospectionQuery {
__Schema {
__schema {
queryType {
name
}
Expand Down
Loading

0 comments on commit 883babd

Please sign in to comment.