graphql_tools/validation/rules/
unique_fragment_names.rs

1use std::collections::HashMap;
2
3use super::ValidationRule;
4use crate::ast::{visit_document, AstNodeWithName, OperationVisitor, OperationVisitorContext};
5use crate::static_graphql::query::*;
6use crate::validation::utils::{ValidationError, ValidationErrorContext};
7
8/// Unique fragment names
9///
10/// A GraphQL document is only valid if all defined fragments have unique names.
11///
12/// See https://spec.graphql.org/draft/#sec-Fragment-Name-Uniqueness
13pub struct UniqueFragmentNames<'a> {
14    findings_counter: HashMap<&'a str, i32>,
15}
16
17impl<'a> OperationVisitor<'a, ValidationErrorContext> for UniqueFragmentNames<'a> {
18    fn enter_fragment_definition(
19        &mut self,
20        _: &mut OperationVisitorContext,
21        _: &mut ValidationErrorContext,
22        fragment: &'a FragmentDefinition,
23    ) {
24        if let Some(name) = fragment.node_name() {
25            self.store_finding(name);
26        }
27    }
28}
29
30impl<'a> Default for UniqueFragmentNames<'a> {
31    fn default() -> Self {
32        Self::new()
33    }
34}
35
36impl<'a> UniqueFragmentNames<'a> {
37    pub fn new() -> Self {
38        Self {
39            findings_counter: HashMap::new(),
40        }
41    }
42
43    fn store_finding(&mut self, name: &'a str) {
44        let value = *self.findings_counter.entry(name).or_insert(0);
45        self.findings_counter.insert(name, value + 1);
46    }
47}
48
49impl<'u> ValidationRule for UniqueFragmentNames<'u> {
50    fn error_code<'a>(&self) -> &'a str {
51        "UniqueFragmentNames"
52    }
53
54    fn validate(
55        &self,
56        ctx: &mut OperationVisitorContext,
57        error_collector: &mut ValidationErrorContext,
58    ) {
59        let mut rule = UniqueFragmentNames::new();
60
61        visit_document(&mut rule, ctx.operation, ctx, error_collector);
62
63        rule.findings_counter
64            .into_iter()
65            .filter(|(_key, value)| *value > 1)
66            .for_each(|(key, _value)| {
67                error_collector.report_error(ValidationError {
68                    error_code: self.error_code(),
69                    message: format!("There can be only one fragment named \"{}\".", key),
70                    locations: vec![],
71                })
72            })
73    }
74}
75
76#[test]
77fn no_fragments() {
78    use crate::validation::test_utils::*;
79
80    let mut plan = create_plan_from_rule(Box::new(UniqueFragmentNames::new()));
81    let errors = test_operation_with_schema(
82        "{
83          field
84        }",
85        TEST_SCHEMA,
86        &mut plan,
87    );
88
89    assert_eq!(get_messages(&errors).len(), 0);
90}
91
92#[test]
93fn one_fragment() {
94    use crate::validation::test_utils::*;
95
96    let mut plan = create_plan_from_rule(Box::new(UniqueFragmentNames::new()));
97    let errors = test_operation_with_schema(
98        "{
99          ...fragA
100        }
101        fragment fragA on Type {
102          field
103        }",
104        TEST_SCHEMA,
105        &mut plan,
106    );
107
108    assert_eq!(get_messages(&errors).len(), 0);
109}
110
111#[test]
112fn many_fragment() {
113    use crate::validation::test_utils::*;
114
115    let mut plan = create_plan_from_rule(Box::new(UniqueFragmentNames::new()));
116    let errors = test_operation_with_schema(
117        "{
118          ...fragA
119          ...fragB
120          ...fragC
121        }
122        fragment fragA on Type {
123          fieldA
124        }
125        fragment fragB on Type {
126          fieldB
127        }
128        fragment fragC on Type {
129          fieldC
130        }",
131        TEST_SCHEMA,
132        &mut plan,
133    );
134
135    assert_eq!(get_messages(&errors).len(), 0);
136}
137
138#[test]
139fn inline_fragments_are_always_unique() {
140    use crate::validation::test_utils::*;
141
142    let mut plan = create_plan_from_rule(Box::new(UniqueFragmentNames::new()));
143    let errors = test_operation_with_schema(
144        "{
145          ...on Type {
146            fieldA
147          }
148          ...on Type {
149            fieldB
150          }
151        }",
152        TEST_SCHEMA,
153        &mut plan,
154    );
155
156    assert_eq!(get_messages(&errors).len(), 0);
157}
158
159#[test]
160fn fragment_and_operation_named_the_same() {
161    use crate::validation::test_utils::*;
162
163    let mut plan = create_plan_from_rule(Box::new(UniqueFragmentNames::new()));
164    let errors = test_operation_with_schema(
165        "query Foo {
166          ...Foo
167        }
168        fragment Foo on Type {
169          field
170        }",
171        TEST_SCHEMA,
172        &mut plan,
173    );
174
175    assert_eq!(get_messages(&errors).len(), 0);
176}
177
178#[test]
179fn fragments_named_the_same() {
180    use crate::validation::test_utils::*;
181
182    let mut plan = create_plan_from_rule(Box::new(UniqueFragmentNames::new()));
183    let errors = test_operation_with_schema(
184        "{
185          ...fragA
186        }
187        fragment fragA on Type {
188          fieldA
189        }
190        fragment fragA on Type {
191          fieldB
192        }",
193        TEST_SCHEMA,
194        &mut plan,
195    );
196
197    let messages = get_messages(&errors);
198    assert_eq!(messages.len(), 1);
199    assert_eq!(
200        messages,
201        vec!["There can be only one fragment named \"fragA\"."]
202    );
203}
204
205#[test]
206fn fragments_named_the_same_without_being_referenced() {
207    use crate::validation::test_utils::*;
208
209    let mut plan = create_plan_from_rule(Box::new(UniqueFragmentNames::new()));
210    let errors = test_operation_with_schema(
211        "fragment fragA on Type {
212          fieldA
213        }
214        fragment fragA on Type {
215          fieldB
216        }",
217        TEST_SCHEMA,
218        &mut plan,
219    );
220
221    let messages = get_messages(&errors);
222    assert_eq!(messages.len(), 1);
223    assert_eq!(
224        messages,
225        vec!["There can be only one fragment named \"fragA\"."]
226    );
227}