1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use std::sync::Arc;
use async_graphql_parser::types::TypeKind;
use crate::{
graphql_query::query::{FieldConnection, FieldNode, Query},
schema::Schema,
};
use super::{
error::{FrontendError, ValidationError},
util::get_underlying_named_type,
};
pub(super) fn validate_query_against_schema(
schema: &Schema,
query: &Query,
) -> Result<(), FrontendError> {
let mut path = vec![];
validate_field(
schema,
schema.query_type_name(),
&mut path,
&query.root_connection,
&query.root_field,
)
}
fn validate_field<'a>(
schema: &Schema,
parent_type_name: &str,
path: &mut Vec<&'a str>,
connection: &FieldConnection,
node: &'a FieldNode,
) -> Result<(), FrontendError> {
assert_eq!(connection.name, node.name);
assert_eq!(connection.alias, node.alias);
let old_path_length = path.len();
let field_def = schema
.fields
.get(&(
Arc::from(parent_type_name.to_string()),
Arc::from(node.name.to_string()),
))
.ok_or_else(|| {
path.push(&node.name);
FrontendError::ValidationError(ValidationError::NonExistentPath(
path.iter().map(|x| x.to_string()).collect(),
))
})?;
path.push(&node.name);
let pre_coercion_type_name = get_underlying_named_type(&field_def.ty.node).as_ref();
let field_type_name = if let Some(coerced) = &node.coerced_to {
let pre_coercion_type_definition = &schema.vertex_types[pre_coercion_type_name];
if let TypeKind::Interface(_) = &pre_coercion_type_definition.kind {
} else {
return Err(FrontendError::ValidationError(
ValidationError::CannotCoerceNonInterfaceType(
pre_coercion_type_name.to_string(),
coerced.to_string(),
),
));
}
if let Some(post_coercion_type_definition) = schema.vertex_types.get(coerced) {
let implemented_interfaces = match &post_coercion_type_definition.kind {
TypeKind::Object(o) => &o.implements,
TypeKind::Interface(i) => &i.implements,
TypeKind::Scalar
| TypeKind::Union(_)
| TypeKind::Enum(_)
| TypeKind::InputObject(_) => unreachable!(),
};
if !implemented_interfaces
.iter()
.any(|x| x.node.as_ref() == pre_coercion_type_name)
{
return Err(FrontendError::ValidationError(
ValidationError::CannotCoerceToUnrelatedType(
pre_coercion_type_name.to_string(),
coerced.to_string(),
),
));
}
} else {
return Err(FrontendError::ValidationError(
ValidationError::NonExistentType(coerced.to_string()),
));
}
path.push(coerced);
coerced.as_ref()
} else {
pre_coercion_type_name
};
for (child_connection, child_node) in node.connections.iter() {
validate_field(schema, field_type_name, path, child_connection, child_node)?;
}
path.pop().unwrap();
if node.coerced_to.is_some() {
path.pop().unwrap();
}
assert_eq!(old_path_length, path.len());
Ok(())
}