graphql_tools/validation/
validate.rs1use super::{
2 rules::ValidationRule,
3 utils::{ValidationError, ValidationErrorContext},
4};
5
6use crate::{
7 ast::OperationVisitorContext,
8 static_graphql::{query, schema},
9};
10
11pub struct ValidationPlan {
12 pub rules: Vec<Box<dyn ValidationRule>>,
13}
14
15impl ValidationPlan {
16 pub fn new() -> Self {
17 Self { rules: vec![] }
18 }
19
20 pub fn from(rules: Vec<Box<dyn ValidationRule>>) -> Self {
21 Self { rules }
22 }
23
24 pub fn add_rule(&mut self, rule: Box<dyn ValidationRule>) {
25 self.rules.push(rule);
26 }
27}
28
29impl Default for ValidationPlan {
30 fn default() -> Self {
31 Self::new()
32 }
33}
34
35pub fn validate<'a>(
36 schema: &'a schema::Document,
37 operation: &'a query::Document,
38 validation_plan: &'a ValidationPlan,
39) -> Vec<ValidationError> {
40 let mut error_collector = ValidationErrorContext::new();
41 let mut validation_context = OperationVisitorContext::new(operation, schema);
42
43 validation_plan
44 .rules
45 .iter()
46 .for_each(|rule| rule.validate(&mut validation_context, &mut error_collector));
47
48 error_collector.errors
49}
50
51#[test]
52fn cyclic_fragment_should_never_loop() {
53 use crate::validation::rules::default_rules_validation_plan;
54 use crate::validation::test_utils::*;
55
56 let mut default_plan = default_rules_validation_plan();
57 let errors = test_operation_with_schema(
58 "
59 {
60 dog {
61 nickname
62 ...bark
63 ...parents
64 }
65 }
66
67 fragment bark on Dog {
68 barkVolume
69 ...parents
70 }
71
72 fragment parents on Dog {
73 mother {
74 ...bark
75 }
76 }
77
78 ",
79 TEST_SCHEMA,
80 &mut default_plan,
81 );
82
83 let messages = get_messages(&errors);
84 assert_eq!(errors[0].error_code, "NoFragmentsCycle");
85 assert_eq!(messages.len(), 1);
86 assert_eq!(
87 messages,
88 vec!["Cannot spread fragment \"bark\" within itself via \"parents\"."]
89 )
90}
91
92#[test]
93fn simple_self_reference_fragment_should_not_loop() {
94 use crate::validation::rules::default_rules_validation_plan;
95 use crate::validation::test_utils::*;
96
97 let mut default_plan = default_rules_validation_plan();
98 let errors = test_operation_with_schema(
99 "
100 query dog {
101 dog {
102 ...DogFields
103 }
104 }
105
106 fragment DogFields on Dog {
107 mother {
108 ...DogFields
109 }
110 father {
111 ...DogFields
112 }
113 }
114 ",
115 TEST_SCHEMA,
116 &mut default_plan,
117 );
118
119 let messages = get_messages(&errors);
120 assert_eq!(messages.len(), 2);
121 assert_eq!(
122 messages,
123 vec![
124 "Cannot spread fragment \"DogFields\" within itself.",
125 "Cannot spread fragment \"DogFields\" within itself."
126 ]
127 )
128}
129
130#[test]
131fn fragment_loop_through_multiple_frags() {
132 use crate::validation::rules::default_rules_validation_plan;
133 use crate::validation::test_utils::*;
134
135 let mut default_plan = default_rules_validation_plan();
136 let errors = test_operation_with_schema(
137 "
138 query dog {
139 dog {
140 ...DogFields1
141 }
142 }
143
144 fragment DogFields1 on Dog {
145 barks
146 ...DogFields2
147 }
148
149 fragment DogFields2 on Dog {
150 barkVolume
151 ...DogFields3
152 }
153
154 fragment DogFields3 on Dog {
155 name
156 ...DogFields1
157 }
158 ",
159 TEST_SCHEMA,
160 &mut default_plan,
161 );
162
163 let messages = get_messages(&errors);
164 assert_eq!(messages.len(), 1);
165 assert_eq!(
166 messages,
167 vec![
168 "Cannot spread fragment \"DogFields1\" within itself via \"DogFields2\", \"DogFields3\"."
169 ]
170 )
171}