async_graphql/validation/
utils.rs1use std::collections::HashSet;
2
3use async_graphql_value::{ConstValue, Value};
4
5use crate::{QueryPathSegment, context::QueryPathNode, registry};
6
7#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
8pub enum Scope<'a> {
9 Operation(Option<&'a str>),
10 Fragment(&'a str),
11}
12
13fn valid_error(path_node: &QueryPathNode, msg: String) -> String {
14 format!("\"{}\", {}", path_node, msg)
15}
16
17pub fn referenced_variables(value: &Value) -> Vec<&str> {
18 let mut vars = Vec::new();
19 referenced_variables_to_vec(value, &mut vars);
20 vars
21}
22
23fn referenced_variables_to_vec<'a>(value: &'a Value, vars: &mut Vec<&'a str>) {
24 match value {
25 Value::Variable(name) => {
26 vars.push(name);
27 }
28 Value::List(values) => values
29 .iter()
30 .for_each(|value| referenced_variables_to_vec(value, vars)),
31 Value::Object(obj) => obj
32 .values()
33 .for_each(|value| referenced_variables_to_vec(value, vars)),
34 _ => {}
35 }
36}
37
38pub fn is_valid_input_value(
39 registry: ®istry::Registry,
40 type_name: &str,
41 value: &ConstValue,
42 path_node: QueryPathNode,
43) -> Option<String> {
44 match registry::MetaTypeName::create(type_name) {
45 registry::MetaTypeName::NonNull(type_name) => match value {
46 ConstValue::Null => Some(valid_error(
47 &path_node,
48 format!("expected type \"{}\"", type_name),
49 )),
50 _ => is_valid_input_value(registry, type_name, value, path_node),
51 },
52 registry::MetaTypeName::List(type_name) => match value {
53 ConstValue::List(elems) => elems.iter().enumerate().find_map(|(idx, elem)| {
54 is_valid_input_value(
55 registry,
56 type_name,
57 elem,
58 QueryPathNode {
59 parent: Some(&path_node),
60 segment: QueryPathSegment::Index(idx),
61 },
62 )
63 }),
64 ConstValue::Null => None,
65 _ => is_valid_input_value(registry, type_name, value, path_node),
66 },
67 registry::MetaTypeName::Named(type_name) => {
68 if let ConstValue::Null = value {
69 return None;
70 }
71
72 match registry
73 .types
74 .get(type_name)
75 .unwrap_or_else(|| panic!("Type `{}` not defined", type_name))
76 {
77 registry::MetaType::Scalar {
78 is_valid: Some(is_valid_fn),
79 ..
80 } => {
81 if (is_valid_fn)(&value) {
82 None
83 } else {
84 Some(valid_error(
85 &path_node,
86 format!("expected type \"{}\"", type_name),
87 ))
88 }
89 }
90 registry::MetaType::Scalar { is_valid: None, .. } => None,
91 registry::MetaType::Enum {
92 enum_values,
93 name: enum_name,
94 ..
95 } => match value {
96 ConstValue::Enum(name) => {
97 if !enum_values.contains_key(name.as_str()) {
98 Some(valid_error(
99 &path_node,
100 format!(
101 "enumeration type \"{}\" does not contain the value \"{}\"",
102 enum_name, name
103 ),
104 ))
105 } else {
106 None
107 }
108 }
109 ConstValue::String(name) => {
110 if !enum_values.contains_key(name.as_str()) {
111 Some(valid_error(
112 &path_node,
113 format!(
114 "enumeration type \"{}\" does not contain the value \"{}\"",
115 enum_name, name
116 ),
117 ))
118 } else {
119 None
120 }
121 }
122 _ => Some(valid_error(
123 &path_node,
124 format!("expected type \"{}\"", type_name),
125 )),
126 },
127 registry::MetaType::InputObject {
128 input_fields,
129 name: object_name,
130 oneof,
131 ..
132 } => match value {
133 ConstValue::Object(values) => {
134 if *oneof {
135 if values.len() != 1 {
136 return Some(valid_error(
137 &path_node,
138 "Oneof input objects requires have exactly one field"
139 .to_string(),
140 ));
141 }
142
143 if let ConstValue::Null = values[0] {
144 return Some(valid_error(
145 &path_node,
146 "Oneof Input Objects require that exactly one field must be supplied and that field must not be null"
147 .to_string(),
148 ));
149 }
150 }
151
152 let mut input_names =
153 values.keys().map(AsRef::as_ref).collect::<HashSet<_>>();
154
155 for field in input_fields.values() {
156 input_names.remove(&*field.name);
157 if let Some(value) = values.get(&*field.name) {
158 if let Some(reason) = is_valid_input_value(
159 registry,
160 &field.ty,
161 value,
162 QueryPathNode {
163 parent: Some(&path_node),
164 segment: QueryPathSegment::Name(&field.name),
165 },
166 ) {
167 return Some(reason);
168 }
169 } else if registry::MetaTypeName::create(&field.ty).is_non_null()
170 && field.default_value.is_none()
171 {
172 return Some(valid_error(
173 &path_node,
174 format!(
175 r#"field "{}" of type "{}" is required but not provided"#,
176 field.name, field.ty,
177 ),
178 ));
179 }
180 }
181
182 if let Some(name) = input_names.iter().next() {
183 return Some(valid_error(
184 &path_node,
185 format!("unknown field \"{}\" of type \"{}\"", name, object_name),
186 ));
187 }
188
189 None
190 }
191 _ => None,
192 },
193 _ => None,
194 }
195 }
196 }
197}