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