use std::collections::HashMap;
use super::ValidationRule;
use crate::ast::{AstNodeWithName, OperationVisitor, OperationVisitorContext};
use crate::static_graphql::query::*;
use crate::validation::utils::{ValidationError, ValidationErrorContext};
pub struct UniqueOperationNames<'doc> {
findings_counter: HashMap<&'doc str, i32>,
}
impl<'doc> OperationVisitor<'doc, ValidationErrorContext> for UniqueOperationNames<'doc> {
fn enter_operation_definition(
&mut self,
_: &mut OperationVisitorContext,
_: &mut ValidationErrorContext,
operation_definition: &'doc OperationDefinition,
) {
if let Some(name) = operation_definition.node_name() {
self.store_finding(name);
}
}
fn leave_document(
&mut self,
_: &mut OperationVisitorContext,
user_context: &mut ValidationErrorContext,
_: &Document,
) {
self.findings_counter
.iter()
.filter(|(_, count)| **count > 1)
.for_each(|(name, _)| {
user_context.report_error(ValidationError {
error_code: self.error_code(),
message: format!("There can be only one operation named \"{}\".", name),
locations: vec![],
});
});
}
}
impl Default for UniqueOperationNames<'_> {
fn default() -> Self {
Self::new()
}
}
impl<'doc> UniqueOperationNames<'doc> {
pub fn new() -> Self {
Self {
findings_counter: HashMap::new(),
}
}
fn store_finding(&mut self, name: &'doc str) {
let value = *self.findings_counter.entry(name).or_insert(0);
self.findings_counter.insert(name, value + 1);
}
}
impl ValidationRule for UniqueOperationNames<'_> {
fn error_code(&self) -> &'static str {
"UniqueOperationNames"
}
fn visitor<'doc>(&self) -> super::ValidationVisitor<'doc> {
Box::new(UniqueOperationNames::new())
}
}
#[test]
fn no_operations() {
use crate::validation::test_utils::*;
let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
let errors = test_operation_with_schema(
"fragment fragA on Type {
field
}",
TEST_SCHEMA,
&mut plan,
);
assert_eq!(get_messages(&errors).len(), 0);
}
#[test]
fn one_anon_operation() {
use crate::validation::test_utils::*;
let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
let errors = test_operation_with_schema(
"{
field
}",
TEST_SCHEMA,
&mut plan,
);
let messages = get_messages(&errors);
assert_eq!(messages.len(), 0);
}
#[test]
fn one_named_operation() {
use crate::validation::test_utils::*;
let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
let errors = test_operation_with_schema(
"query Foo {
field
}",
TEST_SCHEMA,
&mut plan,
);
let messages = get_messages(&errors);
assert_eq!(messages.len(), 0);
}
#[test]
fn multiple_operations() {
use crate::validation::test_utils::*;
let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
let errors = test_operation_with_schema(
"query Foo {
field
}
query Bar {
field
}",
TEST_SCHEMA,
&mut plan,
);
let messages = get_messages(&errors);
assert_eq!(messages.len(), 0);
}
#[test]
fn multiple_operations_of_different_types() {
use crate::validation::test_utils::*;
let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
let errors = test_operation_with_schema(
"query Foo {
field
}
mutation Bar {
field
}
subscription Baz {
field
}",
TEST_SCHEMA,
&mut plan,
);
let messages = get_messages(&errors);
assert_eq!(messages.len(), 0);
}
#[test]
fn fragment_and_operation_named_the_same() {
use crate::validation::test_utils::*;
let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
let errors = test_operation_with_schema(
"query Foo {
...Foo
}
fragment Foo on Type {
field
}",
TEST_SCHEMA,
&mut plan,
);
let messages = get_messages(&errors);
assert_eq!(messages.len(), 0);
}
#[test]
fn multiple_operations_of_same_name() {
use crate::validation::test_utils::*;
let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
let errors = test_operation_with_schema(
"query Foo {
fieldA
}
query Foo {
fieldB
}",
TEST_SCHEMA,
&mut plan,
);
let messages = get_messages(&errors);
assert_eq!(messages.len(), 1);
assert_eq!(
messages,
vec!["There can be only one operation named \"Foo\".",]
);
}
#[test]
fn multiple_ops_of_same_name_of_different_types_mutation() {
use crate::validation::test_utils::*;
let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
let errors = test_operation_with_schema(
"query Foo {
fieldA
}
mutation Foo {
fieldB
}",
TEST_SCHEMA,
&mut plan,
);
let messages = get_messages(&errors);
assert_eq!(messages.len(), 1);
assert_eq!(
messages,
vec!["There can be only one operation named \"Foo\".",]
);
}
#[test]
fn multiple_ops_of_same_name_of_different_types_subscription() {
use crate::validation::test_utils::*;
let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
let errors = test_operation_with_schema(
"query Foo {
fieldA
}
subscription Foo {
fieldB
}",
TEST_SCHEMA,
&mut plan,
);
let messages = get_messages(&errors);
assert_eq!(messages.len(), 1);
assert_eq!(
messages,
vec!["There can be only one operation named \"Foo\".",]
);
}