graphql_tools/validation/
validate.rs

1use 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}