1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use graphql_parser::Pos;
use super::ValidationRule;
use crate::ast::{visit_document, OperationVisitor, OperationVisitorContext};
use crate::static_graphql::query::*;
use crate::validation::utils::{ValidationError, ValidationErrorContext};
pub struct UniqueVariableNames {
found_records: HashMap<String, Pos>,
}
impl UniqueVariableNames {
pub fn new() -> Self {
UniqueVariableNames {
found_records: HashMap::new(),
}
}
}
impl<'a> OperationVisitor<'a, ValidationErrorContext> for UniqueVariableNames {
fn enter_operation_definition(
&mut self,
_: &mut OperationVisitorContext,
_: &mut ValidationErrorContext,
_operation_definition: &OperationDefinition,
) {
self.found_records.clear();
}
fn enter_variable_definition(
&mut self,
_: &mut OperationVisitorContext,
user_context: &mut ValidationErrorContext,
variable_definition: &VariableDefinition,
) {
match self.found_records.entry(variable_definition.name.clone()) {
Entry::Occupied(entry) => user_context.report_error(ValidationError {
locations: vec![*entry.get(), variable_definition.position],
message: format!(
"There can only be one variable named \"${}\".",
variable_definition.name
),
}),
Entry::Vacant(entry) => {
entry.insert(variable_definition.position);
}
};
}
}
impl ValidationRule for UniqueVariableNames {
fn validate<'a>(
&self,
ctx: &'a mut OperationVisitorContext,
error_collector: &mut ValidationErrorContext,
) {
visit_document(
&mut UniqueVariableNames::new(),
&ctx.operation,
ctx,
error_collector,
);
}
}
#[test]
fn unique_variable_names() {
use crate::validation::test_utils::*;
let mut plan = create_plan_from_rule(Box::new(UniqueVariableNames::new()));
let errors = test_operation_with_schema(
"query A($x: Int, $y: String) { __typename }
query B($x: String, $y: Int) { __typename }",
TEST_SCHEMA,
&mut plan,
);
assert_eq!(get_messages(&errors).len(), 0);
}
#[test]
fn duplicate_variable_names() {
use crate::validation::test_utils::*;
let mut plan = create_plan_from_rule(Box::new(UniqueVariableNames::new()));
let errors = test_operation_with_schema(
"query A($x: Int, $x: Int, $x: String) { __typename }
query B($y: String, $y: Int) { __typename }
query C($z: Int, $z: Int) { __typename }",
TEST_SCHEMA,
&mut plan,
);
let messages = get_messages(&errors);
assert_eq!(messages.len(), 4);
assert!(messages.contains(&&"There can only be one variable named \"$x\".".to_owned()));
assert!(messages.contains(&&"There can only be one variable named \"$y\".".to_owned()));
assert!(messages.contains(&&"There can only be one variable named \"$z\".".to_owned()));
}