Skip to content

Commit 21e2e92

Browse files
committed
Fix partially expanded unions and interfaces
We need all variants to be able to deserialize the enum, but some of them may be empty
1 parent bfed867 commit 21e2e92

File tree

6 files changed

+104
-7
lines changed

6 files changed

+104
-7
lines changed

graphql_query_derive/src/interfaces.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
use constants::*;
21
use failure;
32
use objects::GqlObjectField;
43
use proc_macro2::{Ident, Span, TokenStream};
54
use query::QueryContext;
65
use selection::{Selection, SelectionItem};
76
use shared::*;
87
use std::borrow::Cow;
8+
use std::collections::HashSet;
99
use unions::union_variants;
1010

1111
#[derive(Debug, PartialEq)]
1212
pub struct GqlInterface {
13-
pub implemented_by: Vec<String>,
13+
pub implemented_by: HashSet<String>,
1414
pub name: String,
1515
pub fields: Vec<GqlObjectField>,
1616
}
@@ -19,7 +19,7 @@ impl GqlInterface {
1919
pub fn new(name: Cow<str>) -> GqlInterface {
2020
GqlInterface {
2121
name: name.into_owned(),
22-
implemented_by: Vec::new(),
22+
implemented_by: HashSet::new(),
2323
fields: vec![],
2424
}
2525
}
@@ -60,9 +60,19 @@ impl GqlInterface {
6060

6161
let object_children =
6262
field_impls_for_selection(&self.fields, query_context, &object_selection, prefix)?;
63-
let (union_variants, union_children) =
63+
let (mut union_variants, union_children, used_variants) =
6464
union_variants(&union_selection, query_context, prefix)?;
6565

66+
union_variants.extend(
67+
self.implemented_by
68+
.iter()
69+
.filter(|obj| used_variants.iter().find(|v| v == obj).is_none())
70+
.map(|v| {
71+
let v = Ident::new(v, Span::call_site());
72+
quote!(#v)
73+
}),
74+
);
75+
6676
let attached_enum_name = Ident::new(&format!("{}On", name), Span::call_site());
6777
let (attached_enum, last_object_field) = if union_variants.len() > 0 {
6878
let attached_enum = quote! {

graphql_query_derive/src/schema.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,16 @@ impl Schema {
191191

192192
})
193193
}
194+
195+
pub fn ingest_interface_implementations(&mut self, impls: BTreeMap<String, Vec<String>>) {
196+
impls.into_iter().for_each(|(iface_name, implementors)| {
197+
let iface = self
198+
.interfaces
199+
.get_mut(&iface_name)
200+
.expect(&format!("interface not found: {}", iface_name));
201+
iface.implemented_by = implementors.into_iter().collect();
202+
});
203+
}
194204
}
195205

196206
impl ::std::convert::From<graphql_parser::schema::Document> for Schema {
@@ -259,6 +269,8 @@ impl ::std::convert::From<graphql_parser::schema::Document> for Schema {
259269
}
260270
}
261271

272+
schema.ingest_interface_implementations(interface_implementations);
273+
262274
schema
263275
}
264276
}
@@ -356,6 +368,8 @@ impl ::std::convert::From<::introspection_response::IntrospectionResponse> for S
356368
}
357369
}
358370

371+
schema.ingest_interface_implementations(interface_implementations);
372+
359373
schema
360374
}
361375
}

graphql_query_derive/src/unions.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ pub fn union_variants(
2121
selection: &Selection,
2222
query_context: &QueryContext,
2323
prefix: &str,
24-
) -> Result<(Vec<TokenStream>, Vec<TokenStream>), failure::Error> {
24+
) -> Result<(Vec<TokenStream>, Vec<TokenStream>, Vec<String>), failure::Error> {
2525
let mut children_definitions = Vec::new();
26+
let mut used_variants = Vec::with_capacity(selection.0.len());
2627

2728
let variants: Result<Vec<TokenStream>, failure::Error> = selection
2829
.0
@@ -41,6 +42,7 @@ pub fn union_variants(
4142
SelectionItem::FragmentSpread(_) => Err(format_err!("fragment spread on union"))?,
4243
SelectionItem::InlineFragment(frag) => {
4344
let variant_name = Ident::new(&frag.on, Span::call_site());
45+
used_variants.push(frag.on.to_string());
4446

4547
let new_prefix = format!("{}On{}", prefix, frag.on);
4648

@@ -83,7 +85,7 @@ pub fn union_variants(
8385

8486
let variants = variants?;
8587

86-
Ok((variants, children_definitions))
88+
Ok((variants, children_definitions, used_variants))
8789
}
8890

8991
impl GqlUnion {
@@ -103,7 +105,18 @@ impl GqlUnion {
103105
})?;
104106
}
105107

106-
let (variants, children_definitions) = union_variants(selection, query_context, prefix)?;
108+
let (mut variants, children_definitions, used_variants) =
109+
union_variants(selection, query_context, prefix)?;
110+
111+
variants.extend(
112+
self.0
113+
.iter()
114+
.filter(|v| used_variants.iter().find(|a| a == v).is_none())
115+
.map(|v| {
116+
let v = Ident::new(v, Span::call_site());
117+
quote!(#v)
118+
}),
119+
);
107120

108121
Ok(quote!{
109122
#(#children_definitions)*

tests/interfaces.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,29 @@ fn interface_deserialization() {
2828

2929
assert_eq!(response_data.everything.map(|names| names.len()), Some(4));
3030
}
31+
32+
#[derive(GraphQLQuery)]
33+
#[GraphQLQuery(
34+
query_path = "tests/interfaces/interface_not_on_everything_query.graphql",
35+
schema_path = "tests/interfaces/interface_schema.graphql"
36+
)]
37+
#[allow(dead_code)]
38+
struct InterfaceNotOnEverythingQuery;
39+
40+
const RESPONSE_NOT_ON_EVERYTHING: &'static str =
41+
include_str!("interfaces/interface_response_not_on_everything.json");
42+
43+
#[test]
44+
fn interface_not_on_everything_deserialization() {
45+
println!("{:?}", RESPONSE);
46+
let response_data: interface_not_on_everything_query::ResponseData =
47+
serde_json::from_str(RESPONSE_NOT_ON_EVERYTHING).unwrap();
48+
49+
println!("{:?}", response_data);
50+
51+
let expected = r##"ResponseData { everything: Some([RustMyQueryEverything { name: "Audrey Lorde", on: Person(RustMyQueryEverythingOnPerson { birthday: Some("1934-02-18") }) }, RustMyQueryEverything { name: "Laïka", on: Dog }, RustMyQueryEverything { name: "Mozilla", on: Organization(RustMyQueryEverythingOnOrganization { industry: OTHER }) }, RustMyQueryEverything { name: "Norbert", on: Dog }]) }"##;
52+
53+
assert_eq!(format!("{:?}", response_data), expected);
54+
55+
assert_eq!(response_data.everything.map(|names| names.len()), Some(4));
56+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
query MyQuery {
2+
everything {
3+
__typename
4+
name
5+
... on Person {
6+
birthday
7+
}
8+
... on Organization {
9+
industry
10+
}
11+
}
12+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"everything": [
3+
{
4+
"__typename": "Person",
5+
"name": "Audrey Lorde",
6+
"birthday": "1934-02-18"
7+
},
8+
{
9+
"__typename": "Dog",
10+
"name": "Laïka"
11+
},
12+
{
13+
"__typename": "Organization",
14+
"name": "Mozilla",
15+
"industry": "OTHER"
16+
},
17+
{
18+
"__typename": "Dog",
19+
"name": "Norbert"
20+
}
21+
]
22+
}

0 commit comments

Comments
 (0)