Skip to content

Commit e3a9226

Browse files
committed
Test (non-nested) input object codegen
1 parent 87cb608 commit e3a9226

File tree

10 files changed

+199
-42
lines changed

10 files changed

+199
-42
lines changed

graphql_query_derive/src/field_type.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use proc_macro2::{Ident, Span, TokenStream};
55
use query::QueryContext;
66
use schema::DEFAULT_SCALARS;
77

8-
#[derive(Debug, PartialEq)]
8+
#[derive(Debug, PartialEq, Hash)]
99
pub enum FieldType {
1010
Named(Ident),
1111
Optional(Box<FieldType>),
@@ -15,6 +15,11 @@ pub enum FieldType {
1515
impl FieldType {
1616
/// Takes a field type with its name
1717
pub fn to_rust(&self, context: &QueryContext, prefix: &str) -> TokenStream {
18+
let prefix: String = if prefix.is_empty() {
19+
self.inner_name_string()
20+
} else {
21+
prefix.to_string()
22+
};
1823
match &self {
1924
FieldType::Named(name) => {
2025
let name_string = name.to_string();
@@ -32,17 +37,20 @@ impl FieldType {
3237
Span::call_site(),
3338
)
3439
} else {
35-
Ident::new(prefix, Span::call_site())
40+
if prefix.is_empty() {
41+
panic!("Empty prefix for {:?}", self);
42+
}
43+
Ident::new(&prefix, Span::call_site())
3644
};
3745

3846
quote!(#name)
3947
}
4048
FieldType::Optional(inner) => {
41-
let inner = inner.to_rust(context, prefix);
49+
let inner = inner.to_rust(context, &prefix);
4250
quote!( Option<#inner>)
4351
}
4452
FieldType::Vector(inner) => {
45-
let inner = inner.to_rust(context, prefix);
53+
let inner = inner.to_rust(context, &prefix);
4654
quote!( Vec<#inner>)
4755
}
4856
}
@@ -133,6 +141,12 @@ impl ::std::convert::From<introspection_response::FullTypeFieldsType> for FieldT
133141
}
134142
}
135143

144+
impl ::std::convert::From<introspection_response::InputValueType> for FieldType {
145+
fn from(schema_type: introspection_response::InputValueType) -> FieldType {
146+
from_json_type_inner(&schema_type.type_ref, false)
147+
}
148+
}
149+
136150
#[cfg(test)]
137151
mod tests {
138152
use super::*;

graphql_query_derive/src/inputs.rs

Lines changed: 104 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,81 @@
1-
use query::QueryContext;
1+
use failure;
2+
use graphql_parser;
3+
use introspection_response;
24
use objects::GqlObjectField;
35
use proc_macro2::{Ident, Span, TokenStream};
4-
use introspection_response;
5-
use graphql_parser;
6+
use query::QueryContext;
7+
use std::collections::HashMap;
68

79
#[derive(Debug, PartialEq)]
810
pub struct GqlInput {
911
pub name: String,
10-
pub fields: Vec<GqlObjectField>,
12+
pub fields: HashMap<String, GqlObjectField>,
1113
}
1214

1315
impl GqlInput {
14-
pub fn to_rust(&self, context: &QueryContext) -> TokenStream {
16+
pub fn to_rust(&self, context: &QueryContext) -> Result<TokenStream, failure::Error> {
1517
let name = Ident::new(&self.name, Span::call_site());
16-
let fields = self.fields.iter().map(|field| {
18+
let mut fields: Vec<&GqlObjectField> = self.fields.values().collect();
19+
fields.sort_unstable_by(|a, b| a.name.cmp(&b.name));
20+
let fields = fields.iter().map(|field| {
1721
let ty = field.type_.to_rust(&context, "");
1822
let name = Ident::new(&field.name, Span::call_site());
19-
quote!(#ty: #name)
23+
quote!(#name: #ty)
2024
});
2125

22-
quote!{
26+
Ok(quote! {
2327
#[derive(Debug, Serialize)]
2428
#[serde(rename_all = "camelCase")]
2529
pub struct #name {
2630
#(#fields,)*
2731
}
28-
}
32+
})
2933
}
3034
}
3135

3236
impl ::std::convert::From<graphql_parser::schema::InputObjectType> for GqlInput {
3337
fn from(schema_input: graphql_parser::schema::InputObjectType) -> GqlInput {
34-
unimplemented!();
38+
GqlInput {
39+
name: schema_input.name,
40+
fields: schema_input
41+
.fields
42+
.into_iter()
43+
.map(|field| {
44+
let name = field.name.clone();
45+
let field = GqlObjectField {
46+
name: field.name,
47+
type_: field.value_type.into(),
48+
};
49+
(name, field)
50+
})
51+
.collect(),
52+
}
3553
}
3654
}
3755

3856
impl ::std::convert::From<introspection_response::FullType> for GqlInput {
3957
fn from(schema_input: introspection_response::FullType) -> GqlInput {
40-
unimplemented!();
58+
GqlInput {
59+
name: schema_input.name.expect("unnamed input object"),
60+
fields: schema_input
61+
.input_fields
62+
.expect("fields on input object")
63+
.into_iter()
64+
.filter_map(|a| a)
65+
.map(|f| {
66+
let name = f.input_value.name.expect("unnamed input object field");
67+
let field = GqlObjectField {
68+
name: name.clone(),
69+
type_: f
70+
.input_value
71+
.type_
72+
.expect("type on input object field")
73+
.into(),
74+
};
75+
(name, field)
76+
})
77+
.collect(),
78+
}
4179
}
4280
}
4381

@@ -49,30 +87,66 @@ mod tests {
4987

5088
#[test]
5189
fn gql_input_to_rust() {
52-
let input = GqlInput {
90+
let cat = GqlInput {
5391
name: "Cat".to_string(),
5492
fields: vec![
55-
GqlObjectField {
56-
name: "pawsCount".to_string(),
57-
type_: FieldType::Named(float_type())
58-
},
59-
GqlObjectField {
60-
name: "offsprings".to_string(),
61-
type_: FieldType::Vector(Box::new(FieldType::Named(Ident::new("Cat", Span::call_site())))),
62-
},
63-
GqlObjectField {
64-
name: "requirements".to_string(),
65-
type_: FieldType::Optional(Box::new(FieldType::Named(Ident::new("CatRequirements", Span::call_site())))),
66-
},
67-
],
93+
(
94+
"pawsCount".to_string(),
95+
GqlObjectField {
96+
name: "pawsCount".to_string(),
97+
type_: FieldType::Named(float_type()),
98+
},
99+
),
100+
(
101+
"offsprings".to_string(),
102+
GqlObjectField {
103+
name: "offsprings".to_string(),
104+
type_: FieldType::Vector(Box::new(FieldType::Named(Ident::new(
105+
"Cat",
106+
Span::call_site(),
107+
)))),
108+
},
109+
),
110+
(
111+
"requirements".to_string(),
112+
GqlObjectField {
113+
name: "requirements".to_string(),
114+
type_: FieldType::Optional(Box::new(FieldType::Named(Ident::new(
115+
"CatRequirements",
116+
Span::call_site(),
117+
)))),
118+
},
119+
),
120+
].into_iter()
121+
.collect(),
68122
};
69123

70-
71124
let expected: String = vec![
72-
"",
73-
"",
74-
].into_iter().collect();
125+
"# [ derive ( Debug , Serialize ) ] ",
126+
"# [ serde ( rename_all = \"camelCase\" ) ] ",
127+
"pub struct Cat { ",
128+
"offsprings : Vec < Cat > , ",
129+
"pawsCount : Float , ",
130+
"requirements : Option < CatRequirements > , ",
131+
"}",
132+
].into_iter()
133+
.collect();
134+
135+
let mut context = QueryContext::new_empty();
136+
context.schema.inputs.insert(cat.name.clone(), cat);
75137

76-
assert_eq!(format!("{:?}", input.to_rust()), expected);
138+
assert_eq!(
139+
format!(
140+
"{}",
141+
context
142+
.schema
143+
.inputs
144+
.get("Cat")
145+
.unwrap()
146+
.to_rust(&context)
147+
.unwrap()
148+
),
149+
expected
150+
);
77151
}
78152
}

graphql_query_derive/src/introspection_response.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ pub struct FullTypeFields {
169169
#[serde(rename_all = "camelCase")]
170170
pub struct FullTypeInputFields {
171171
#[serde(flatten)]
172-
input_value: InputValue,
172+
pub input_value: InputValue,
173173
}
174174

175175
#[derive(Clone, Debug, Deserialize)]
@@ -198,11 +198,11 @@ pub struct FullTypePossibleTypes {
198198
#[derive(Clone, Debug, Deserialize)]
199199
#[serde(rename_all = "camelCase")]
200200
pub struct InputValue {
201-
name: Option<String>,
202-
description: Option<String>,
201+
pub name: Option<String>,
202+
pub description: Option<String>,
203203
#[serde(rename = "type")]
204-
type_: Option<InputValueType>,
205-
default_value: Option<String>,
204+
pub type_: Option<InputValueType>,
205+
pub default_value: Option<String>,
206206
}
207207

208208
#[derive(Clone, Debug, Deserialize)]

graphql_query_derive/src/objects.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub struct GqlObject {
1414
pub fields: Vec<GqlObjectField>,
1515
}
1616

17-
#[derive(Debug, PartialEq)]
17+
#[derive(Debug, PartialEq, Hash)]
1818
pub struct GqlObjectField {
1919
pub name: String,
2020
pub type_: FieldType,

graphql_query_derive/src/schema.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,9 @@ impl ::std::convert::From<graphql_parser::schema::Document> for Schema {
235235
);
236236
}
237237
schema::TypeDefinition::InputObject(input) => {
238-
schema.inputs.insert(input.name.clone(), GqlInput::from(input));
238+
schema
239+
.inputs
240+
.insert(input.name.clone(), GqlInput::from(input));
239241
}
240242
},
241243
schema::Definition::DirectiveDefinition(_) => (),

graphql_query_derive/src/tests/github.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ fn ast_from_graphql_and_json_produce_the_same_schema() {
2525
assert_eq!(json.query_type, gql.query_type);
2626
assert_eq!(json.mutation_type, gql.mutation_type);
2727
assert_eq!(json.subscription_type, gql.subscription_type);
28-
assert_eq!(json.inputs, gql.inputs);
28+
for (json, gql) in json.inputs.iter().zip(gql.inputs.iter()) {
29+
assert_eq!(json, gql);
30+
}
31+
assert_eq!(json.inputs, gql.inputs, "inputs differ");
2932
for ((json_name, json_value), (gql_name, gql_value)) in json.enums.iter().zip(gql.enums.iter())
3033
{
3134
assert_eq!(json_name, gql_name);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#[macro_use]
2+
extern crate graphql_client;
3+
#[macro_use]
4+
extern crate serde_derive;
5+
extern crate serde;
6+
extern crate serde_json;
7+
8+
#[derive(GraphQLQuery)]
9+
#[GraphQLQuery(
10+
query_path = "tests/scalar_variables_query.graphql",
11+
schema_path = "tests/scalar_variables_schema.graphql"
12+
)]
13+
#[allow(dead_code)]
14+
struct ScalarVariablesQuery;
15+
16+
#[test]
17+
fn scalar_variables_query_variables_struct() {
18+
scalar_variables_query::Variables {
19+
msg: "hello".to_string(),
20+
reps: Some(32),
21+
};
22+
}
23+
24+
#[derive(GraphQLQuery)]
25+
#[GraphQLQuery(
26+
query_path = "tests/scalar_variables_query_defaults.graphql",
27+
schema_path = "tests/scalar_variables_schema.graphql"
28+
)]
29+
#[allow(dead_code)]
30+
struct DefaultScalarVariablesQuery;
31+
32+
#[test]
33+
fn scalar_variables_default() {
34+
let variables = default_scalar_variables_query::Variables {
35+
msg: default_scalar_variables_query::Variables::default_msg(),
36+
reps: default_scalar_variables_query::Variables::default_reps(),
37+
};
38+
39+
let out = serde_json::to_string(&variables).unwrap();
40+
41+
assert_eq!(out, r#"{"msg":"o, hai","reps":3}"#);
42+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
query VariablesQuery($msg: String = "o, hai", $reps: Int = 3) {
2+
echo(message: $msg, repetitions: $reps) {
3+
result
4+
}
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
query VariablesQuery($msg: String!, $reps: Int) {
2+
echo(message: $msg, repetitions: $reps) {
3+
result
4+
}
5+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
schema {
3+
query: ScalarVariablesQuery
4+
}
5+
6+
type ScalarVariablesQuery {
7+
echo(message: String!, repetitions: Int): EchoResult
8+
}
9+
10+
type EchoResult {
11+
result: String!
12+
}

0 commit comments

Comments
 (0)