Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions graphql_client_codegen/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,12 @@ impl Schema {
.expect("Schema::get_object")
}

pub(crate) fn get_object_mut(&mut self, object_id: ObjectId) -> &mut StoredObject {
self.stored_objects
.get_mut(object_id.0 as usize)
.expect("Schema::get_object_mut")
}

pub(crate) fn get_field(&self, field_id: StoredFieldId) -> &StoredField {
self.stored_fields.get(field_id.0).unwrap()
}
Expand Down
52 changes: 51 additions & 1 deletion graphql_client_codegen/src/schema/graphql_parser_conversion.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use super::{Schema, StoredInputFieldType, TypeId};
use crate::schema::resolve_field_type;
use graphql_parser::schema::{self as parser, Definition, Document, TypeDefinition, UnionType};
use graphql_parser::schema::{
self as parser, Definition, Document, TypeDefinition, TypeExtension, UnionType,
};

pub(super) fn build_schema<'doc, T>(
mut src: graphql_parser::schema::Document<'doc, T>,
Expand Down Expand Up @@ -36,6 +38,8 @@ where
interfaces_mut(src).for_each(|iface| ingest_interface(schema, iface));

objects_mut(src).for_each(|obj| ingest_object(schema, obj));
extend_object_type_extensions_mut(src)
.for_each(|ext| ingest_object_type_extension(schema, ext));

inputs_mut(src).for_each(|input| ingest_input(schema, input));

Expand Down Expand Up @@ -200,6 +204,40 @@ fn ingest_object<'doc, T>(
schema.push_object(object);
}

fn ingest_object_type_extension<'doc, T>(
schema: &mut Schema,
ext: &mut graphql_parser::schema::ObjectTypeExtension<'doc, T>,
) where
T: graphql_parser::query::Text<'doc>,
{
let object_id = schema
.find_type_id(ext.name.as_ref())
.as_object_id()
.unwrap();
let mut field_ids = Vec::with_capacity(ext.fields.len());

for field in ext.fields.iter_mut() {
let field = super::StoredField {
name: field.name.as_ref().into(),
r#type: resolve_field_type(schema, &field.field_type),
parent: super::StoredFieldParent::Object(object_id),
deprecation: find_deprecation(&field.directives),
};

field_ids.push(schema.push_field(field));
}

let iface_ids = ext
.implements_interfaces
.iter()
.map(|iface_name| schema.find_interface(iface_name.as_ref()))
.collect::<Vec<_>>();

let object = schema.get_object_mut(object_id);
object.implements_interfaces.extend(iface_ids);
object.fields.extend(field_ids);
}

fn ingest_scalar<'doc, T>(
schema: &mut Schema,
scalar: &mut graphql_parser::schema::ScalarType<'doc, T>,
Expand Down Expand Up @@ -328,6 +366,18 @@ where
})
}

fn extend_object_type_extensions_mut<'a, 'doc: 'a, T>(
doc: &'a mut Document<'doc, T>,
) -> impl Iterator<Item = &'a mut parser::ObjectTypeExtension<'doc, T>>
where
T: graphql_parser::query::Text<'doc>,
{
doc.definitions.iter_mut().filter_map(|def| match def {
Definition::TypeExtension(TypeExtension::Object(obj)) => Some(obj),
_ => None,
})
}

fn interfaces_mut<'a, 'doc: 'a, T>(
doc: &'a mut Document<'doc, T>,
) -> impl Iterator<Item = &'a mut parser::InterfaceType<'doc, T>>
Expand Down
1 change: 1 addition & 0 deletions graphql_client_codegen/src/schema/tests.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
mod extend_object;
mod github;
129 changes: 129 additions & 0 deletions graphql_client_codegen/src/schema/tests/extend_object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use crate::schema::Schema;

const SCHEMA_JSON: &str = include_str!("extend_object_schema.json");
const SCHEMA_GRAPHQL: &str = include_str!("extend_object_schema.graphql");

#[test]
fn ast_from_graphql_and_json_produce_the_same_schema() {
let json: graphql_introspection_query::introspection_response::IntrospectionResponse =
serde_json::from_str(SCHEMA_JSON).unwrap();
let graphql_parser_schema = graphql_parser::parse_schema(SCHEMA_GRAPHQL)
.unwrap()
.into_static();
let mut json = Schema::from(json);
let mut gql = Schema::from(graphql_parser_schema);

assert!(vecs_match(&json.stored_scalars, &gql.stored_scalars));

// Root objects
{
assert_eq!(
json.get_object(json.query_type()).name,
gql.get_object(gql.query_type()).name
);
assert_eq!(
json.mutation_type().map(|t| &json.get_object(t).name),
gql.mutation_type().map(|t| &gql.get_object(t).name),
"Mutation types don't match."
);
assert_eq!(
json.subscription_type().map(|t| &json.get_object(t).name),
gql.subscription_type().map(|t| &gql.get_object(t).name),
"Subscription types don't match."
);
}

// Objects
{
let mut json_stored_objects: Vec<_> = json
.stored_objects
.drain(..)
.filter(|obj| !obj.name.starts_with("__"))
.collect();

assert_eq!(
json_stored_objects.len(),
gql.stored_objects.len(),
"Objects count matches."
);

json_stored_objects.sort_by(|a, b| a.name.cmp(&b.name));
gql.stored_objects.sort_by(|a, b| a.name.cmp(&b.name));

for (j, g) in json_stored_objects
.iter_mut()
.filter(|obj| !obj.name.starts_with("__"))
.zip(gql.stored_objects.iter_mut())
{
assert_eq!(j.name, g.name);
assert_eq!(
j.implements_interfaces.len(),
g.implements_interfaces.len(),
"{}",
j.name
);
assert_eq!(j.fields.len(), g.fields.len(), "{}", j.name);
}
}

// Unions
{
assert_eq!(json.stored_unions.len(), gql.stored_unions.len());

json.stored_unions.sort_by(|a, b| a.name.cmp(&b.name));
gql.stored_unions.sort_by(|a, b| a.name.cmp(&b.name));

for (json, gql) in json.stored_unions.iter().zip(gql.stored_unions.iter()) {
assert_eq!(json.variants.len(), gql.variants.len());
}
}

// Interfaces
{
assert_eq!(json.stored_interfaces.len(), gql.stored_interfaces.len());

json.stored_interfaces.sort_by(|a, b| a.name.cmp(&b.name));
gql.stored_interfaces.sort_by(|a, b| a.name.cmp(&b.name));

for (json, gql) in json
.stored_interfaces
.iter()
.zip(gql.stored_interfaces.iter())
{
assert_eq!(json.fields.len(), gql.fields.len());
}
}

// Input objects
{
json.stored_enums = json
.stored_enums
.drain(..)
.filter(|enm| !enm.name.starts_with("__"))
.collect();
assert_eq!(json.stored_inputs.len(), gql.stored_inputs.len());

json.stored_inputs.sort_by(|a, b| a.name.cmp(&b.name));
gql.stored_inputs.sort_by(|a, b| a.name.cmp(&b.name));

for (json, gql) in json.stored_inputs.iter().zip(gql.stored_inputs.iter()) {
assert_eq!(json.fields.len(), gql.fields.len());
}
}

// Enums
{
assert_eq!(json.stored_enums.len(), gql.stored_enums.len());

json.stored_enums.sort_by(|a, b| a.name.cmp(&b.name));
gql.stored_enums.sort_by(|a, b| a.name.cmp(&b.name));

for (json, gql) in json.stored_enums.iter().zip(gql.stored_enums.iter()) {
assert_eq!(json.variants.len(), gql.variants.len());
}
}
}

fn vecs_match<T: PartialEq>(a: &[T], b: &[T]) -> bool {
a.len() == b.len() && a.iter().all(|a| b.iter().any(|b| a == b))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
schema {
query: Query
}

type Query {
foo: String
}

extend type Query {
bar: Int
}
Loading