Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add known operation types rule #39

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/validation/rules/defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
140 changes: 140 additions & 0 deletions src/validation/rules/known_operation_types.rs
Original file line number Diff line number Diff line change
@@ -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};

/// Known operation types
///
/// A GraphQL operation is only valid if the operation type is within the schema.
///
/// See https://github.com/graphql/graphql-spec/pull/947
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."]
);
}
2 changes: 2 additions & 0 deletions src/validation/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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::*;