Skip to content

Commit 880b061

Browse files
committed
Bring schema building from json and graphql to feature equivalence
1 parent 7263172 commit 880b061

File tree

7 files changed

+41073
-169
lines changed

7 files changed

+41073
-169
lines changed

graphql_query_derive/src/field_type.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use enums::ENUMS_PREFIX;
22
use graphql_parser::schema;
3+
use introspection_response;
34
use proc_macro2::{Ident, Span, TokenStream};
45
use query::QueryContext;
56
use schema::DEFAULT_SCALARS;
6-
use introspection_response;
77

88
#[derive(Debug, PartialEq)]
99
pub enum FieldType {
@@ -98,15 +98,13 @@ fn from_json_type_inner(inner: &introspection_response::TypeRef, non_null: bool)
9898
from_json_type_inner(&inner.of_type.expect("inner type is missing"), true)
9999
}
100100
Some(__TypeKind::LIST) => {
101-
from_json_type_inner(&inner.of_type.expect("inner type is missing"), false)
102-
}
103-
Some(_) => {
104-
FieldType::Named(Ident::new(&inner.name.expect("type name"), Span::call_site()))
101+
FieldType::Vector(Box::new(from_json_type_inner(&inner.of_type.expect("inner type is missing"), false)))
105102
}
106-
None => {
107-
unreachable!("non-convertible type")
108-
}
109-
103+
Some(_) => FieldType::Named(Ident::new(
104+
&inner.name.expect("type name"),
105+
Span::call_site(),
106+
)),
107+
None => unreachable!("non-convertible type"),
110108
};
111109

112110
if non_null {

graphql_query_derive/src/introspection_response.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ pub struct FullTypeInputFields {
176176
#[serde(rename_all = "camelCase")]
177177
pub struct FullTypeInterfaces {
178178
#[serde(flatten)]
179-
type_ref: TypeRef,
179+
pub type_ref: TypeRef,
180180
}
181181

182182
#[derive(Clone, Debug, Deserialize)]
@@ -310,10 +310,10 @@ pub struct RustIntrospectionQuerySchemaDirectivesArgs {
310310
#[derive(Clone, Debug, Deserialize)]
311311
#[serde(rename_all = "camelCase")]
312312
pub struct RustIntrospectionQuerySchemaDirectives {
313-
name: Option<String>,
314-
description: Option<String>,
315-
locations: Option<Vec<Option<__DirectiveLocation>>>,
316-
args: Option<Vec<Option<RustIntrospectionQuerySchemaDirectivesArgs>>>,
313+
pub name: Option<String>,
314+
pub description: Option<String>,
315+
pub locations: Option<Vec<Option<__DirectiveLocation>>>,
316+
pub args: Option<Vec<Option<RustIntrospectionQuerySchemaDirectivesArgs>>>,
317317
}
318318

319319
#[derive(Clone, Debug, Deserialize)]
@@ -332,7 +332,4 @@ pub struct Schema {
332332
pub schema: Option<RustIntrospectionQuerySchema>,
333333
}
334334

335-
#[derive(Clone, Debug, Deserialize)]
336-
pub struct IntrospectionResponse {
337-
pub data: Schema,
338-
}
335+
pub type IntrospectionResponse = Schema;

graphql_query_derive/src/schema.rs

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use proc_macro2::TokenStream;
1010
use query::QueryContext;
1111
use std::collections::{BTreeMap, BTreeSet};
1212
use unions::GqlUnion;
13-
use introspection_response;
1413

1514
pub const DEFAULT_SCALARS: &[&'static str] = &["ID", "String", "Int", "Float", "Boolean"];
1615

@@ -221,7 +220,8 @@ impl ::std::convert::From<graphql_parser::schema::Document> for Schema {
221220
schema.scalars.insert(scalar.name);
222221
}
223222
schema::TypeDefinition::Union(union) => {
224-
schema.unions.insert(union.name, GqlUnion(union.types));
223+
let tys: BTreeSet<String> = union.types.into_iter().collect();
224+
schema.unions.insert(union.name, GqlUnion(tys));
225225
}
226226
schema::TypeDefinition::Interface(interface) => {
227227
schema.interfaces.insert(
@@ -263,7 +263,11 @@ impl ::std::convert::From<::introspection_response::IntrospectionResponse> for S
263263
use introspection_response::__TypeKind;
264264

265265
let mut schema = Schema::new();
266-
let root = src.data.schema.expect("__Schema is not null");
266+
let root = src.schema.expect("__Schema is not null");
267+
268+
// Holds which objects implement which interfaces so we can populate GqlInterface#implemented_by later.
269+
// It maps interface names to a vec of implementation names.
270+
let mut interface_implementations: BTreeMap<String, Vec<String>> = BTreeMap::new();
267271

268272
for ty in root
269273
.types
@@ -288,10 +292,12 @@ impl ::std::convert::From<::introspection_response::IntrospectionResponse> for S
288292
.insert(name.clone(), GqlEnum { name, variants });
289293
}
290294
Some(__TypeKind::SCALAR) => {
291-
schema.scalars.insert(name);
295+
if DEFAULT_SCALARS.iter().find(|s| s == &&name.as_str()).is_none() {
296+
schema.scalars.insert(name);
297+
}
292298
}
293299
Some(__TypeKind::UNION) => {
294-
let variants: Vec<String> = ty
300+
let variants: BTreeSet<String> = ty
295301
.possible_types
296302
.clone()
297303
.unwrap()
@@ -301,25 +307,57 @@ impl ::std::convert::From<::introspection_response::IntrospectionResponse> for S
301307
schema.unions.insert(name.clone(), GqlUnion(variants));
302308
}
303309
Some(__TypeKind::OBJECT) => {
310+
for implementing in ty
311+
.interfaces
312+
.clone()
313+
.unwrap_or_else(|| Vec::new())
314+
.into_iter()
315+
.filter_map(|t| t)
316+
.map(|t| t.type_ref.name)
317+
{
318+
interface_implementations
319+
.entry(implementing.expect("interface name"))
320+
.and_modify(|objects| objects.push(name.clone()))
321+
.or_insert_with(|| vec![name.clone()]);
322+
}
323+
304324
let fields: Vec<GqlObjectField> = ty
305325
.fields
306326
.clone()
307327
.unwrap()
308328
.into_iter()
309-
.filter_map(|t| t.map(|t| GqlObjectField {
310-
name: t.name.expect("field name"),
311-
type_: FieldType::from(t.type_.expect("field type"))
312-
}))
329+
.filter_map(|t| {
330+
t.map(|t| GqlObjectField {
331+
name: t.name.expect("field name"),
332+
type_: FieldType::from(t.type_.expect("field type")),
333+
})
334+
})
313335
.collect();
314-
schema.objects.insert(name.clone(), GqlObject {
315-
name,
316-
fields,
317-
});
336+
schema
337+
.objects
338+
.insert(name.clone(), GqlObject { name, fields });
318339
}
319340
Some(__TypeKind::INTERFACE) => {
320-
},
341+
let iface = GqlInterface {
342+
name: name.clone(),
343+
implemented_by: Vec::new(),
344+
fields: ty
345+
.fields
346+
.clone()
347+
.expect("interface fields")
348+
.into_iter()
349+
.filter_map(|f| f)
350+
.map(|f| GqlObjectField {
351+
name: f.name.expect("field name"),
352+
type_: FieldType::from(f.type_.expect("field type")),
353+
})
354+
.collect(),
355+
};
356+
schema.interfaces.insert(name, iface);
357+
}
321358
Some(__TypeKind::INPUT_OBJECT) => {
322-
},
359+
schema.inputs.insert(name, GqlInput);
360+
}
323361
_ => unimplemented!("unimplemented definition"),
324362
}
325363
}
Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,36 @@
11
use graphql_parser;
22
use schema::Schema;
33
use serde_json;
4+
use std::collections::HashSet;
45

56
const SCHEMA_JSON: &'static str = include_str!("github_schema.json");
67
const SCHEMA_GRAPHQL: &'static str = include_str!("github_schema.graphql");
78

89
#[test]
910
fn ast_from_graphql_and_json_produce_the_same_schema() {
11+
use ::std::iter::FromIterator;
1012
let json: ::introspection_response::IntrospectionResponse =
1113
serde_json::from_str(SCHEMA_JSON).unwrap();
1214
let graphql_parser_schema = graphql_parser::parse_schema(SCHEMA_GRAPHQL).unwrap();
13-
assert_eq!(Schema::from(graphql_parser_schema), Schema::from(json),);
15+
let json = Schema::from(json);
16+
let gql = Schema::from(graphql_parser_schema);
17+
assert_eq!(json.scalars, gql.scalars);
18+
for (json, gql) in json.objects.iter().zip(gql.objects.iter()) {
19+
assert_eq!(json, gql)
20+
}
21+
for (json, gql) in json.unions.iter().zip(gql.unions.iter()) {
22+
assert_eq!(json, gql)
23+
}
24+
assert_eq!(json.interfaces, gql.interfaces);
25+
assert_eq!(json.query_type, gql.query_type);
26+
assert_eq!(json.mutation_type, gql.mutation_type);
27+
assert_eq!(json.subscription_type, gql.subscription_type);
28+
assert_eq!(json.inputs, gql.inputs);
29+
for ((json_name, json_value), (gql_name, gql_value)) in json.enums.iter().zip(gql.enums.iter()) {
30+
assert_eq!(json_name, gql_name);
31+
assert_eq!(
32+
HashSet::<&String>::from_iter(json_value.variants.iter()),
33+
HashSet::<&String>::from_iter(gql_value.variants.iter()),
34+
);
35+
}
1436
}

0 commit comments

Comments
 (0)