Skip to content

Fix issues from #83 #84

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Aug 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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