Skip to content

Commit f6ce7dd

Browse files
committed
added SingleFieldSubscriptions rule
1 parent 098824f commit f6ce7dd

12 files changed

+755
-69
lines changed

src/ast/collect_fields.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
use std::collections::HashMap;
2+
3+
use super::{AbstractTypeDefinitionExtension, TypeInfoRegistry};
4+
use crate::ast::ext::{TypeDefinitionExtension, UnionTypeExtension};
5+
use crate::static_graphql::{
6+
query::{self, Selection, TypeCondition},
7+
schema::{self, TypeDefinition},
8+
};
9+
pub fn collect_fields(
10+
selection_set: &query::SelectionSet,
11+
parent_type: &schema::TypeDefinition,
12+
known_fragments: &HashMap<String, query::FragmentDefinition>,
13+
type_info_registry: &TypeInfoRegistry,
14+
) -> HashMap<String, Vec<query::Field>> {
15+
let mut map = HashMap::new();
16+
let mut visited_fragments_names: Vec<String> = Vec::new();
17+
18+
collect_fields_inner(
19+
selection_set,
20+
parent_type,
21+
known_fragments,
22+
type_info_registry,
23+
&mut map,
24+
&mut visited_fragments_names,
25+
);
26+
27+
map
28+
}
29+
30+
fn does_fragment_condition_match<'a>(
31+
fragment_condition: &'a Option<TypeCondition>,
32+
current_selection_set_type: &'a TypeDefinition,
33+
type_info_registry: &'a TypeInfoRegistry<'a>,
34+
) -> bool {
35+
if let Some(TypeCondition::On(type_name)) = fragment_condition {
36+
if let Some(conditional_type) = type_info_registry.type_by_name.get(type_name) {
37+
println!("conditional_type: {}", conditional_type.name());
38+
println!(
39+
"current_selection_set_type: {}",
40+
current_selection_set_type.name()
41+
);
42+
if conditional_type
43+
.name()
44+
.eq(&current_selection_set_type.name())
45+
{
46+
return true;
47+
}
48+
49+
if conditional_type.is_abstract_type() {
50+
match conditional_type {
51+
TypeDefinition::Interface(interface_type) => {
52+
return interface_type.is_implemented_by(current_selection_set_type)
53+
}
54+
TypeDefinition::Union(union_type) => {
55+
return union_type.has_sub_type(&current_selection_set_type.name())
56+
}
57+
_ => return false,
58+
}
59+
}
60+
}
61+
62+
false
63+
} else {
64+
true
65+
}
66+
}
67+
68+
fn collect_fields_inner(
69+
selection_set: &query::SelectionSet,
70+
parent_type: &schema::TypeDefinition,
71+
known_fragments: &HashMap<String, query::FragmentDefinition>,
72+
type_info_registry: &TypeInfoRegistry,
73+
result_arr: &mut HashMap<String, Vec<query::Field>>,
74+
visited_fragments_names: &mut Vec<String>,
75+
) {
76+
println!("collect_fields_inner");
77+
selection_set.items.iter().for_each(|item| match item {
78+
Selection::Field(f) => {
79+
let existing = result_arr.entry(f.name.clone()).or_insert(vec![]);
80+
existing.push(f.clone());
81+
}
82+
Selection::InlineFragment(f) => {
83+
if does_fragment_condition_match(&f.type_condition, parent_type, type_info_registry) {
84+
collect_fields_inner(
85+
&f.selection_set,
86+
&parent_type,
87+
known_fragments,
88+
type_info_registry,
89+
result_arr,
90+
visited_fragments_names,
91+
);
92+
}
93+
}
94+
Selection::FragmentSpread(f) => {
95+
if visited_fragments_names
96+
.iter()
97+
.find(|name| f.fragment_name.eq(*name))
98+
.is_none()
99+
{
100+
visited_fragments_names.push(f.fragment_name.clone());
101+
102+
if let Some(fragment) = known_fragments.get(&f.fragment_name) {
103+
if does_fragment_condition_match(
104+
&Some(fragment.type_condition.clone()),
105+
&parent_type,
106+
type_info_registry,
107+
) {
108+
collect_fields_inner(
109+
&fragment.selection_set,
110+
&parent_type,
111+
known_fragments,
112+
type_info_registry,
113+
result_arr,
114+
visited_fragments_names,
115+
);
116+
}
117+
}
118+
}
119+
}
120+
});
121+
}

src/ast/ext.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use crate::static_graphql::query::{self};
2-
use crate::static_graphql::schema::{self, Field, InterfaceType, ObjectType, UnionType};
2+
use crate::static_graphql::schema::{
3+
self, Field, InterfaceType, ObjectType, TypeDefinition, UnionType,
4+
};
35

46
use super::{get_named_type, TypeInfoElementRef};
57

@@ -70,13 +72,64 @@ impl CompositeType {
7072
}
7173
}
7274

75+
pub trait AbstractTypeDefinitionExtension {
76+
fn is_implemented_by(&self, other_type: &dyn ImplementingInterfaceExtension) -> bool;
77+
}
78+
7379
pub trait TypeDefinitionExtension {
7480
fn is_leaf_type(&self) -> bool;
7581
fn is_composite_type(&self) -> bool;
7682
fn is_input_type(&self) -> bool;
83+
fn is_abstract_type(&self) -> bool;
7784
fn name(&self) -> String;
7885
}
7986

87+
pub trait ImplementingInterfaceExtension {
88+
fn interfaces(&self) -> Vec<String>;
89+
}
90+
91+
impl ImplementingInterfaceExtension for TypeDefinition {
92+
fn interfaces(&self) -> Vec<String> {
93+
match self {
94+
schema::TypeDefinition::Object(o) => o.interfaces(),
95+
schema::TypeDefinition::Interface(i) => i.interfaces(),
96+
_ => vec![],
97+
}
98+
}
99+
}
100+
101+
impl ImplementingInterfaceExtension for InterfaceType {
102+
fn interfaces(&self) -> Vec<String> {
103+
self.implements_interfaces.clone()
104+
}
105+
}
106+
107+
impl ImplementingInterfaceExtension for ObjectType {
108+
fn interfaces(&self) -> Vec<String> {
109+
self.implements_interfaces.clone()
110+
}
111+
}
112+
113+
pub trait UnionTypeExtension {
114+
fn has_sub_type(&self, other_type_name: &String) -> bool;
115+
}
116+
117+
impl UnionTypeExtension for UnionType {
118+
fn has_sub_type(&self, other_type_name: &String) -> bool {
119+
self.types.iter().find(|v| other_type_name.eq(*v)).is_some()
120+
}
121+
}
122+
123+
impl AbstractTypeDefinitionExtension for InterfaceType {
124+
fn is_implemented_by(&self, other_type: &dyn ImplementingInterfaceExtension) -> bool {
125+
other_type
126+
.interfaces()
127+
.iter()
128+
.find(|v| self.name.eq(*v))
129+
.is_some()
130+
}
131+
}
132+
80133
impl TypeDefinitionExtension for CompositeType {
81134
fn is_leaf_type(&self) -> bool {
82135
false
@@ -97,6 +150,14 @@ impl TypeDefinitionExtension for CompositeType {
97150
CompositeType::Union(u) => u.name.clone(),
98151
}
99152
}
153+
154+
fn is_abstract_type(&self) -> bool {
155+
match self {
156+
CompositeType::Object(_o) => false,
157+
CompositeType::Interface(_i) => true,
158+
CompositeType::Union(_u) => true,
159+
}
160+
}
100161
}
101162

102163
impl TypeDefinitionExtension for schema::TypeDefinition {
@@ -111,6 +172,17 @@ impl TypeDefinitionExtension for schema::TypeDefinition {
111172
}
112173
}
113174

175+
fn is_abstract_type(&self) -> bool {
176+
match self {
177+
schema::TypeDefinition::Object(_o) => false,
178+
schema::TypeDefinition::Interface(_i) => true,
179+
schema::TypeDefinition::Union(_u) => true,
180+
schema::TypeDefinition::Scalar(_u) => false,
181+
schema::TypeDefinition::Enum(_u) => false,
182+
schema::TypeDefinition::InputObject(_u) => false,
183+
}
184+
}
185+
114186
fn is_leaf_type(&self) -> bool {
115187
match self {
116188
schema::TypeDefinition::Object(_o) => false,

src/ast/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod ast_visitor;
2+
pub mod collect_fields;
23
pub mod ext;
34
pub mod query_visitor;
45
/// Utilities visiting GraphQL AST trees
@@ -7,6 +8,7 @@ pub mod type_info;
78
pub mod type_info_query_visitor;
89
pub mod utils;
910

11+
pub use self::collect_fields::*;
1012
pub use self::ext::*;
1113
pub use self::query_visitor::*;
1214
pub use self::schema_visitor::*;

src/ast/type_info.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ impl<'a> TypeInfoRegistry<'a> {
3333
&schema,
3434
match schema_definition {
3535
Some(schema_definition) => schema_definition
36-
.query
36+
.mutation
3737
.clone()
3838
.unwrap_or("Mutation".to_string()),
3939
None => "Mutation".to_string(),
@@ -43,7 +43,7 @@ impl<'a> TypeInfoRegistry<'a> {
4343
&schema,
4444
match schema_definition {
4545
Some(schema_definition) => schema_definition
46-
.query
46+
.subscription
4747
.clone()
4848
.unwrap_or("Subscription".to_string()),
4949
None => "Subscription".to_string(),

0 commit comments

Comments
 (0)