Skip to content

Commit 9594d09

Browse files
authored
Merge pull request #69 from tomhoule/doctests
Add doctests on GraphQLResponse and GraphQLError
2 parents 471d1f5 + 7b9c3a3 commit 9594d09

File tree

10 files changed

+192
-17
lines changed

10 files changed

+192
-17
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
77

88
## Unreleased
99

10+
## [0.3.0] - 2018-07-24
11+
1012
### Added
1113

1214
- Implemented support for the `extensions` field on errors from the June 2018 spec (#64).
15+
- Improved documentation crate docs, added doctests and examples
16+
17+
### Fixed
1318

19+
- `Location` fields on errors were not public.
20+
- The field names on input objects were not properly converted between snake and camel case.
1421

1522
### Changed
1623

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "graphql_client"
3-
version = "0.2.0"
3+
version = "0.3.0"
44
authors = ["Tom Houlé <[email protected]>"]
55
description = "Typed GraphQL requests and responses"
66
repository = "https://github.com/tomhoule/graphql-client"
@@ -11,7 +11,7 @@ categories = ["network-programming", "web-programming", "wasm"]
1111
[dependencies]
1212
failure = "0.1"
1313
quote = "0.3"
14-
graphql_query_derive = {path = "./graphql_query_derive", version = "0.2.0"}
14+
graphql_query_derive = {path = "./graphql_query_derive", version = "0.3.0"}
1515
graphql-parser = "0.2.0"
1616
serde = "1.0"
1717
serde_derive = "1.0"

graphql_client_cli/Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "graphql_client_cli"
33
description = "The CLI for graphql-client (WIP)"
4-
version = "0.2.0"
4+
version = "0.3.0"
55
authors = ["Tom Houlé <[email protected]>"]
66
license = "Apache-2.0 OR MIT"
77

@@ -12,7 +12,7 @@ path = "src/main.rs"
1212
[dependencies]
1313
failure = "0.1"
1414
reqwest = "0.8"
15-
graphql_client = { version = "0.2.0", path = ".." }
15+
graphql_client = { version = "0.3.0", path = ".." }
1616
structopt = "0.2"
1717
serde = "1.0"
1818
serde_derive = "1.0"

graphql_query_derive/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "graphql_query_derive"
3-
version = "0.2.0"
3+
version = "0.3.0"
44
authors = ["Tom Houlé <[email protected]>"]
55
description = "Utility crate for graphql_client"
66
license = "Apache-2.0 OR MIT"

graphql_query_derive/src/inputs.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use failure;
22
use graphql_parser;
3+
use heck::SnakeCase;
34
use introspection_response;
45
use objects::GqlObjectField;
56
use proc_macro2::{Ident, Span, TokenStream};
@@ -21,7 +22,7 @@ impl GqlInput {
2122
fields.sort_unstable_by(|a, b| a.name.cmp(&b.name));
2223
let fields = fields.iter().map(|field| {
2324
let ty = field.type_.to_rust(&context, "");
24-
let name = Ident::new(&field.name, Span::call_site());
25+
let name = Ident::new(&field.name.to_snake_case(), Span::call_site());
2526
quote!(pub #name: #ty)
2627
});
2728

@@ -136,7 +137,7 @@ mod tests {
136137
"# [ serde ( rename_all = \"camelCase\" ) ] ",
137138
"pub struct Cat { ",
138139
"pub offsprings : Vec < Cat > , ",
139-
"pub pawsCount : Float , ",
140+
"pub paws_count : Float , ",
140141
"pub requirements : Option < CatRequirements > , ",
141142
"}",
142143
].into_iter()

graphql_query_derive/src/query.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use failure;
22
use fragments::GqlFragment;
33
use graphql_parser;
4+
use heck::SnakeCase;
45
use proc_macro2::{Ident, Span, TokenStream};
56
use schema::Schema;
67
use selection::Selection;
@@ -43,7 +44,7 @@ impl QueryContext {
4344
let fields = self.variables.iter().map(|variable| {
4445
let name = &variable.name;
4546
let ty = variable.ty.to_rust(self, "");
46-
let name = Ident::new(name, Span::call_site());
47+
let name = Ident::new(&name.to_snake_case(), Span::call_site());
4748
quote!(pub #name: #ty)
4849
});
4950

@@ -54,6 +55,7 @@ impl QueryContext {
5455

5556
quote! {
5657
#[derive(Serialize)]
58+
#[serde(rename_all = "camelCase")]
5759
pub struct Variables {
5860
#(#fields,)*
5961
}

graphql_query_derive/src/schema.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ mod tests {
477477

478478
#[test]
479479
fn build_schema_works() {
480-
let gql_schema = include_str!("star_wars_schema.graphql");
480+
let gql_schema = include_str!("tests/star_wars_schema.graphql");
481481
let gql_schema = graphql_parser::parse_schema(gql_schema).unwrap();
482482
let built = Schema::from(gql_schema);
483483
assert_eq!(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
query StarWarsQuery($episodeForHero: Episode!) {
2+
hero(episode: $episodeForHero) {
3+
name
4+
__typename
5+
}
6+
}

src/lib.rs

+167-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! The top-level documentation resides on the [project README](https://github.com/tomhoule/graphql-client) at the moment.
22
//!
3-
//! The main interface to this library is the custom derive that generates modules from a GraphQL query and schema.
3+
//! The main interface to this library is the custom derive that generates modules from a GraphQL query and schema. See the docs for the [`GraphQLQuery`] trait for a full example.
44
55
#![deny(missing_docs)]
66

@@ -20,7 +20,52 @@ use std::collections::HashMap;
2020

2121
/// A convenience trait that can be used to build a GraphQL request body.
2222
///
23-
/// This will be implemented for you by codegen in the normal case.
23+
/// This will be implemented for you by codegen in the normal case. It is implemented on the struct you place the derive on.
24+
///
25+
/// Example:
26+
///
27+
///
28+
/// ```
29+
/// extern crate failure;
30+
/// #[macro_use]
31+
/// extern crate graphql_client;
32+
/// #[macro_use]
33+
/// extern crate serde_derive;
34+
/// #[macro_use]
35+
/// extern crate serde_json;
36+
/// extern crate serde;
37+
///
38+
/// #[derive(GraphQLQuery)]
39+
/// #[graphql(
40+
/// query_path = "graphql_query_derive/src/tests/star_wars_query.graphql",
41+
/// schema_path = "graphql_query_derive/src/tests/star_wars_schema.graphql"
42+
/// )]
43+
/// struct StarWarsQuery;
44+
///
45+
/// fn main() -> Result<(), failure::Error> {
46+
/// use graphql_client::GraphQLQuery;
47+
///
48+
/// let variables = star_wars_query::Variables {
49+
/// episode_for_hero: star_wars_query::Episode::NEWHOPE,
50+
/// };
51+
///
52+
/// let expected_body = json!({
53+
/// "query": star_wars_query::QUERY,
54+
/// "variables": {
55+
/// "episodeForHero": "NEWHOPE"
56+
/// },
57+
/// });
58+
///
59+
/// let actual_body = serde_json::to_value(
60+
/// StarWarsQuery::build_query(variables)
61+
/// )?;
62+
///
63+
/// assert_eq!(actual_body, expected_body);
64+
///
65+
/// Ok(())
66+
/// }
67+
/// ```
68+
/// ```
2469
pub trait GraphQLQuery<'de> {
2570
/// The shape of the variables expected by the query. This should be a generated struct most of the time.
2671
type Variables: serde::Serialize;
@@ -31,7 +76,7 @@ pub trait GraphQLQuery<'de> {
3176
fn build_query(variables: Self::Variables) -> GraphQLQueryBody<Self::Variables>;
3277
}
3378

34-
/// The form in which queries are sent over HTTP in most implemnetations.
79+
/// The form in which queries are sent over HTTP in most implementations. This will be built using the [`GraphQLQuery`] trait normally.
3580
#[derive(Debug, Serialize, Deserialize)]
3681
pub struct GraphQLQueryBody<Variables>
3782
where
@@ -43,14 +88,16 @@ where
4388
pub query: &'static str,
4489
}
4590

46-
/// Represents a location inside a query string. Used in errors.
91+
/// Represents a location inside a query string. Used in errors. See [`GraphQLError`].
4792
#[derive(Debug, Serialize, Deserialize, PartialEq)]
4893
pub struct Location {
49-
line: i32,
50-
column: i32,
94+
/// The line number in the query string where the error originated (starting from 1).
95+
pub line: i32,
96+
/// The column number in the query string where the error originated (starting from 1).
97+
pub column: i32,
5198
}
5299

53-
/// Part of a path in a query. It can be an object key or an array index.
100+
/// Part of a path in a query. It can be an object key or an array index. See [`GraphQLError`].
54101
#[derive(Debug, Serialize, Deserialize, PartialEq)]
55102
#[serde(untagged)]
56103
pub enum PathFragment {
@@ -65,6 +112,69 @@ pub enum PathFragment {
65112
/// This tries to be as close to the spec as possible.
66113
///
67114
/// [Spec](https://github.com/facebook/graphql/blob/master/spec/Section%207%20--%20Response.md)
115+
///
116+
///
117+
/// ```
118+
/// # extern crate failure;
119+
/// # #[macro_use]
120+
/// # extern crate serde_json;
121+
/// # extern crate graphql_client;
122+
/// # #[macro_use]
123+
/// # extern crate serde_derive;
124+
/// #
125+
/// # #[derive(Debug, Deserialize, PartialEq)]
126+
/// # struct ResponseData {
127+
/// # something: i32
128+
/// # }
129+
/// #
130+
/// # fn main() -> Result<(), failure::Error> {
131+
/// use graphql_client::*;
132+
///
133+
/// let body: GraphQLResponse<ResponseData> = serde_json::from_value(json!({
134+
/// "data": null,
135+
/// "errors": [
136+
/// {
137+
/// "message": "The server crashed. Sorry.",
138+
/// "locations": [{ "line": 1, "column": 1 }]
139+
/// },
140+
/// {
141+
/// "message": "Seismic activity detected",
142+
/// "path": ["undeground", 20]
143+
/// },
144+
/// ],
145+
/// }))?;
146+
///
147+
/// let expected: GraphQLResponse<ResponseData> = GraphQLResponse {
148+
/// data: None,
149+
/// errors: Some(vec![
150+
/// GraphQLError {
151+
/// message: "The server crashed. Sorry.".to_owned(),
152+
/// locations: Some(vec![
153+
/// Location {
154+
/// line: 1,
155+
/// column: 1,
156+
/// }
157+
/// ]),
158+
/// path: None,
159+
/// extensions: None,
160+
/// },
161+
/// GraphQLError {
162+
/// message: "Seismic activity detected".to_owned(),
163+
/// locations: None,
164+
/// path: Some(vec![
165+
/// PathFragment::Key("undeground".into()),
166+
/// PathFragment::Index(20),
167+
/// ]),
168+
/// extensions: None,
169+
/// },
170+
/// ]),
171+
/// };
172+
///
173+
/// assert_eq!(body, expected);
174+
///
175+
/// # Ok(())
176+
/// # }
177+
/// ```
68178
#[derive(Debug, Serialize, Deserialize, PartialEq)]
69179
pub struct GraphQLError {
70180
/// The human-readable error message. This is the only required field.
@@ -82,7 +192,56 @@ pub struct GraphQLError {
82192
/// This will generally be used with the `ResponseData` struct from a derived module.
83193
///
84194
/// [Spec](https://github.com/facebook/graphql/blob/master/spec/Section%207%20--%20Response.md)
85-
#[derive(Debug, Serialize, Deserialize)]
195+
///
196+
/// ```
197+
/// # extern crate failure;
198+
/// # #[macro_use]
199+
/// # extern crate serde_json;
200+
/// # extern crate graphql_client;
201+
/// # #[macro_use]
202+
/// # extern crate serde_derive;
203+
/// #
204+
/// # #[derive(Debug, Deserialize, PartialEq)]
205+
/// # struct User {
206+
/// # id: i32,
207+
/// # }
208+
/// #
209+
/// # #[derive(Debug, Deserialize, PartialEq)]
210+
/// # struct Dog {
211+
/// # name: String
212+
/// # }
213+
/// #
214+
/// # #[derive(Debug, Deserialize, PartialEq)]
215+
/// # struct ResponseData {
216+
/// # users: Vec<User>,
217+
/// # dogs: Vec<Dog>,
218+
/// # }
219+
/// #
220+
/// # fn main() -> Result<(), failure::Error> {
221+
/// use graphql_client::GraphQLResponse;
222+
///
223+
/// let body: GraphQLResponse<ResponseData> = serde_json::from_value(json!({
224+
/// "data": {
225+
/// "users": [{"id": 13}],
226+
/// "dogs": [{"name": "Strelka"}],
227+
/// },
228+
/// "errors": [],
229+
/// }))?;
230+
///
231+
/// let expected: GraphQLResponse<ResponseData> = GraphQLResponse {
232+
/// data: Some(ResponseData {
233+
/// users: vec![User { id: 13 }],
234+
/// dogs: vec![Dog { name: "Strelka".to_owned() }],
235+
/// }),
236+
/// errors: Some(vec![]),
237+
/// };
238+
///
239+
/// assert_eq!(body, expected);
240+
///
241+
/// # Ok(())
242+
/// # }
243+
/// ```
244+
#[derive(Debug, Serialize, Deserialize, PartialEq)]
86245
pub struct GraphQLResponse<Data> {
87246
/// The absent, partial or complete response data.
88247
pub data: Option<Data>,

0 commit comments

Comments
 (0)