From 6aa2cd75733b2b27883fad53f466df1c4b4e762c Mon Sep 17 00:00:00 2001 From: Laurin Quast Date: Wed, 25 May 2022 11:13:47 +0200 Subject: [PATCH 1/5] feat: add known operation types rule --- src/validation/rules/defaults.rs | 4 +- src/validation/rules/known_operation_types.rs | 140 ++++++++++++++++++ src/validation/rules/mod.rs | 2 + 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 src/validation/rules/known_operation_types.rs diff --git a/src/validation/rules/defaults.rs b/src/validation/rules/defaults.rs index d26bc2e..647a273 100644 --- a/src/validation/rules/defaults.rs +++ b/src/validation/rules/defaults.rs @@ -7,7 +7,7 @@ use super::{ OverlappingFieldsCanBeMerged, PossibleFragmentSpreads, ProvidedRequiredArguments, SingleFieldSubscriptions, UniqueArgumentNames, UniqueDirectivesPerLocation, UniqueFragmentNames, UniqueOperationNames, UniqueVariableNames, ValuesOfCorrectType, - VariablesAreInputTypes, VariablesInAllowedPosition, + VariablesAreInputTypes, VariablesInAllowedPosition, KnownOperationTypes }; pub fn default_rules_validation_plan() -> ValidationPlan { @@ -37,6 +37,8 @@ pub fn default_rules_validation_plan() -> ValidationPlan { plan.add_rule(Box::new(VariablesInAllowedPosition::new())); plan.add_rule(Box::new(ValuesOfCorrectType::new())); plan.add_rule(Box::new(UniqueDirectivesPerLocation::new())); + plan.add_rule(Box::new(UniqueDirectivesPerLocation::new())); + plan.add_rule(Box::new(KnownOperationTypes::new())); plan } diff --git a/src/validation/rules/known_operation_types.rs b/src/validation/rules/known_operation_types.rs new file mode 100644 index 0000000..e820e22 --- /dev/null +++ b/src/validation/rules/known_operation_types.rs @@ -0,0 +1,140 @@ +use super::ValidationRule; +use crate::ast::{ visit_document, OperationVisitor, OperationVisitorContext, SchemaDocumentExtension, + }; +use crate::static_graphql::query::*; +use crate::validation::utils::{ValidationError, ValidationErrorContext}; + +/// Unique variable names +/// +/// A GraphQL operation is only valid if all its variables are uniquely named. +/// +/// See https://spec.graphql.org/draft/#sec-Variable-Uniqueness +pub struct KnownOperationTypes; + +impl KnownOperationTypes { + pub fn new() -> Self { + KnownOperationTypes + } +} + +fn build_error_message(root_type_name: &str) -> String { + format!("The {} operation is not supported by the schema.", root_type_name) +} + +impl<'a> OperationVisitor<'a, ValidationErrorContext> for KnownOperationTypes { + fn enter_operation_definition( + &mut self, + visitor_context: &mut OperationVisitorContext, + user_context: &mut ValidationErrorContext, + operation_definition: &OperationDefinition, + ) { + match operation_definition { + OperationDefinition::Mutation(mutation) => { + if let None = visitor_context.schema.mutation_type() { + user_context.report_error(ValidationError { + locations: vec![mutation.position], + message: build_error_message("mutation"), + }); + } + }, + OperationDefinition::Subscription(subscription) => { + if let None = visitor_context.schema.subscription_type() { + user_context.report_error(ValidationError { + locations: vec![subscription.position], + message: build_error_message("subscription"), + }); + } + }, + OperationDefinition::SelectionSet(_) => {}, + OperationDefinition::Query(_) => {}, + } + } +} + +impl ValidationRule for KnownOperationTypes { + fn validate<'a>( + &self, + ctx: &'a mut OperationVisitorContext, + error_collector: &mut ValidationErrorContext, + ) { + visit_document( + &mut KnownOperationTypes::new(), + &ctx.operation, + ctx, + error_collector, + ); + } +} + +#[test] +fn one_known_operation() { + use crate::validation::test_utils::*; + + let mut plan = create_plan_from_rule(Box::new(KnownOperationTypes {})); + let errors = test_operation_with_schema( + "{ field }", + TEST_SCHEMA, + &mut plan, + ); + + assert_eq!(get_messages(&errors).len(), 0); +} + +#[test] +fn unknown_mutation_operation() { + use crate::validation::test_utils::*; + + let mut plan = create_plan_from_rule(Box::new(KnownOperationTypes {})); + let errors = test_operation_with_schema( + "mutation { field }", + "type Query { _: String }", + &mut plan, + ); + + let messages = get_messages(&errors); + assert_eq!(messages.len(), 1); + assert_eq!( + messages, + vec!["The mutation operation is not supported by the schema."] + ); +} + +#[test] +fn unknown_subscription_operation() { + use crate::validation::test_utils::*; + + let mut plan = create_plan_from_rule(Box::new(KnownOperationTypes {})); + let errors = test_operation_with_schema( + "subscription { field }", + "type Query { _: String }", + &mut plan, + ); + + let messages = get_messages(&errors); + assert_eq!(messages.len(), 1); + assert_eq!( + messages, + vec!["The subscription operation is not supported by the schema."] + ); +} + +#[test] +fn mixture_of_known_and_unknown_operations() { + use crate::validation::test_utils::*; + + let mut plan = create_plan_from_rule(Box::new(KnownOperationTypes {})); + let errors = test_operation_with_schema( + "query { field } + mutation { field } + subscription { field }", + "type Query { field: String }", + &mut plan, + ); + + let messages = get_messages(&errors); + assert_eq!(messages.len(), 2); + assert_eq!( + messages, + vec!["The mutation operation is not supported by the schema.", "The subscription operation is not supported by the schema."] + ); +} \ No newline at end of file diff --git a/src/validation/rules/mod.rs b/src/validation/rules/mod.rs index 44446e1..c722119 100644 --- a/src/validation/rules/mod.rs +++ b/src/validation/rules/mod.rs @@ -25,6 +25,7 @@ pub mod unique_variable_names; pub mod values_of_correct_type; pub mod variables_are_input_types; pub mod variables_in_allowed_position; +pub mod known_operation_types; pub use self::defaults::*; pub use self::rule::*; @@ -53,3 +54,4 @@ pub use self::unique_variable_names::*; pub use self::values_of_correct_type::*; pub use self::variables_are_input_types::*; pub use self::variables_in_allowed_position::*; +pub use self::known_operation_types::*; From a09641de7ea87f36a04a1e246eaca05649c65b84 Mon Sep 17 00:00:00 2001 From: Laurin Quast Date: Wed, 25 May 2022 11:15:37 +0200 Subject: [PATCH 2/5] fix: update docs --- src/validation/rules/known_operation_types.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/validation/rules/known_operation_types.rs b/src/validation/rules/known_operation_types.rs index e820e22..65bda67 100644 --- a/src/validation/rules/known_operation_types.rs +++ b/src/validation/rules/known_operation_types.rs @@ -4,11 +4,11 @@ use crate::ast::{ visit_document, OperationVisitor, OperationVisitorContext, use crate::static_graphql::query::*; use crate::validation::utils::{ValidationError, ValidationErrorContext}; -/// Unique variable names +/// Known operation types /// -/// A GraphQL operation is only valid if all its variables are uniquely named. +/// A GraphQL operation is only valid if the operation type is within the schema. /// -/// See https://spec.graphql.org/draft/#sec-Variable-Uniqueness +/// See https://github.com/graphql/graphql-spec/pull/947 pub struct KnownOperationTypes; impl KnownOperationTypes { From 53cf95e953bbfdfb2cabc089697c9f7dfbfefa18 Mon Sep 17 00:00:00 2001 From: Laurin Quast Date: Wed, 25 May 2022 11:21:13 +0200 Subject: [PATCH 3/5] chore: shorten code --- src/validation/rules/known_operation_types.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/validation/rules/known_operation_types.rs b/src/validation/rules/known_operation_types.rs index 65bda67..1dbda63 100644 --- a/src/validation/rules/known_operation_types.rs +++ b/src/validation/rules/known_operation_types.rs @@ -45,8 +45,7 @@ impl<'a> OperationVisitor<'a, ValidationErrorContext> for KnownOperationTypes { }); } }, - OperationDefinition::SelectionSet(_) => {}, - OperationDefinition::Query(_) => {}, + _ => {} } } } From 2eb9fc8fdaa63afb62cc3996a49b6a937c7a4a54 Mon Sep 17 00:00:00 2001 From: Laurin Quast Date: Wed, 25 May 2022 11:22:04 +0200 Subject: [PATCH 4/5] style: add trailing new line --- src/validation/rules/known_operation_types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/validation/rules/known_operation_types.rs b/src/validation/rules/known_operation_types.rs index 1dbda63..df7534d 100644 --- a/src/validation/rules/known_operation_types.rs +++ b/src/validation/rules/known_operation_types.rs @@ -136,4 +136,4 @@ fn mixture_of_known_and_unknown_operations() { messages, vec!["The mutation operation is not supported by the schema.", "The subscription operation is not supported by the schema."] ); -} \ No newline at end of file +} From 2b8c0313f7259e8e4e30bceb212ad72f480b7433 Mon Sep 17 00:00:00 2001 From: Laurin Quast Date: Wed, 25 May 2022 11:23:01 +0200 Subject: [PATCH 5/5] refactor: use better name for function parameter --- src/validation/rules/known_operation_types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/validation/rules/known_operation_types.rs b/src/validation/rules/known_operation_types.rs index df7534d..abcc41f 100644 --- a/src/validation/rules/known_operation_types.rs +++ b/src/validation/rules/known_operation_types.rs @@ -17,8 +17,8 @@ impl KnownOperationTypes { } } -fn build_error_message(root_type_name: &str) -> String { - format!("The {} operation is not supported by the schema.", root_type_name) +fn build_error_message(operation_type: &str) -> String { + format!("The {} operation is not supported by the schema.", operation_type) } impl<'a> OperationVisitor<'a, ValidationErrorContext> for KnownOperationTypes {