Skip to content

Commit d10b5b4

Browse files
committed
Add the implicit __typename field to all objects
It is part of the spec: https://github.com/facebook/graphql/blob/master/spec/Section%204%20--%20Introspection.md
1 parent 053abf2 commit d10b5b4

File tree

4 files changed

+59
-25
lines changed

4 files changed

+59
-25
lines changed

graphql_query_derive/src/constants.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use proc_macro2::{Ident, Span};
2+
3+
pub const TYPENAME_FIELD: &'static str = "__typename";
4+
5+
pub fn string_type() -> Ident {
6+
Ident::new("String", Span::call_site())
7+
}

graphql_query_derive/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ extern crate quote;
1616

1717
use proc_macro2::TokenStream;
1818

19+
mod constants;
1920
mod enums;
2021
mod field_type;
2122
mod fragments;

graphql_query_derive/src/objects.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
use constants::*;
12
use failure;
23
use field_type::FieldType;
34
use heck::{CamelCase, SnakeCase};
45
use proc_macro2::{Ident, Span, TokenStream};
56
use query::QueryContext;
67
use selection::*;
78
use shared::render_object_field;
9+
use std::borrow::Cow;
810

911
#[derive(Debug, PartialEq)]
1012
pub struct GqlObject {
@@ -19,6 +21,47 @@ pub struct GqlObjectField {
1921
}
2022

2123
impl GqlObject {
24+
pub fn new(name: Cow<str>) -> GqlObject {
25+
GqlObject {
26+
name: name.into_owned(),
27+
fields: vec![GqlObjectField {
28+
name: TYPENAME_FIELD.to_string(),
29+
/// Non-nullable, see spec:
30+
/// https://github.com/facebook/graphql/blob/master/spec/Section%204%20--%20Introspection.md
31+
type_: FieldType::Named(string_type()),
32+
}],
33+
}
34+
}
35+
36+
pub fn from_graphql_parser_object(obj: ::graphql_parser::schema::ObjectType) -> Self {
37+
let mut item = GqlObject::new(obj.name.into());
38+
item.fields.extend(obj.fields.iter()
39+
.map(|f| GqlObjectField {
40+
name: f.name.clone(),
41+
type_: FieldType::from(f.field_type.clone()),
42+
}));
43+
item
44+
}
45+
46+
pub fn from_introspected_schema_json(obj: &::introspection_response::FullType) -> Self {
47+
let mut item = GqlObject::new(obj.name.clone().expect("missing object name").into());
48+
let fields = obj
49+
.fields
50+
.clone()
51+
.unwrap()
52+
.into_iter()
53+
.filter_map(|t| {
54+
t.map(|t| GqlObjectField {
55+
name: t.name.expect("field name"),
56+
type_: FieldType::from(t.type_.expect("field type")),
57+
})
58+
});
59+
60+
item.fields.extend(fields);
61+
62+
item
63+
}
64+
2265
pub fn response_for_selection(
2366
&self,
2467
query_context: &QueryContext,
@@ -47,7 +90,6 @@ impl GqlObject {
4790
selection
4891
.0
4992
.iter()
50-
.filter(|selected| selected.name() != "__typename")
5193
.map(|selected| {
5294
if let SelectionItem::Field(selected) = selected {
5395
let ty = self
@@ -82,6 +124,7 @@ impl GqlObject {
82124
match item {
83125
SelectionItem::Field(f) => {
84126
let name = &f.name;
127+
85128
let ty = &self
86129
.fields
87130
.iter()

graphql_query_derive/src/schema.rs

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -194,17 +194,7 @@ impl ::std::convert::From<graphql_parser::schema::Document> for Schema {
194194

195195
schema.objects.insert(
196196
obj.name.clone(),
197-
GqlObject {
198-
name: obj.name.clone(),
199-
fields: obj
200-
.fields
201-
.iter()
202-
.map(|f| GqlObjectField {
203-
name: f.name.clone(),
204-
type_: FieldType::from(f.field_type.clone()),
205-
})
206-
.collect(),
207-
},
197+
GqlObject::from_graphql_parser_object(obj),
208198
);
209199
}
210200
schema::TypeDefinition::Enum(enm) => {
@@ -325,21 +315,9 @@ impl ::std::convert::From<::introspection_response::IntrospectionResponse> for S
325315
.or_insert_with(|| vec![name.clone()]);
326316
}
327317

328-
let fields: Vec<GqlObjectField> = ty
329-
.fields
330-
.clone()
331-
.unwrap()
332-
.into_iter()
333-
.filter_map(|t| {
334-
t.map(|t| GqlObjectField {
335-
name: t.name.expect("field name"),
336-
type_: FieldType::from(t.type_.expect("field type")),
337-
})
338-
})
339-
.collect();
340318
schema
341319
.objects
342-
.insert(name.clone(), GqlObject { name, fields });
320+
.insert(name.clone(), GqlObject::from_introspected_schema_json(ty));
343321
}
344322
Some(__TypeKind::INTERFACE) => {
345323
let iface = GqlInterface {
@@ -372,6 +350,7 @@ impl ::std::convert::From<::introspection_response::IntrospectionResponse> for S
372350

373351
#[cfg(test)]
374352
mod tests {
353+
use constants::*;
375354
use super::*;
376355
use proc_macro2::{Ident, Span};
377356

@@ -385,6 +364,10 @@ mod tests {
385364
Some(&GqlObject {
386365
name: "Droid".to_string(),
387366
fields: vec![
367+
GqlObjectField {
368+
name: TYPENAME_FIELD.to_string(),
369+
type_: FieldType::Named(string_type()),
370+
},
388371
GqlObjectField {
389372
name: "id".to_string(),
390373
type_: FieldType::Optional(Box::new(FieldType::Named(Ident::new(

0 commit comments

Comments
 (0)