use crate::validation::visitor::{Visitor, VisitorContext}; use graphql_parser::query::Field; #[derive(Default)] pub struct ScalarLeafs; impl<'a> Visitor<'a> for ScalarLeafs { fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Field) { if let Some(ty) = ctx.parent_type() { if let Some(schema_field) = ty.field_by_name(&field.name) { if let Some(ty) = ctx.registry.basic_type_by_typename(&schema_field.ty) { if ty.is_leaf() && !field.selection_set.items.is_empty() { ctx.report_error(vec![field.position], format!( "Field \"{}\" must not have a selection since type \"{}\" has no subfields", field.name, ty.name() )) } else if !ty.is_leaf() && field.selection_set.items.is_empty() { ctx.report_error( vec![field.position], format!( "Field \"{}\" of type \"{}\" must have a selection of subfields", field.name, ty.name() ), ) } } } } } } #[cfg(test)] mod tests { use super::*; use crate::validation::test_harness::{expect_fails_rule, expect_passes_rule}; pub fn factory() -> ScalarLeafs { ScalarLeafs } #[test] fn valid_scalar_selection() { expect_passes_rule( factory, r#" fragment scalarSelection on Dog { barks } "#, ); } #[test] fn object_type_missing_selection() { expect_fails_rule( factory, r#" query directQueryOnObjectWithoutSubFields { human } "#, ); } #[test] fn interface_type_missing_selection() { expect_fails_rule( factory, r#" { human { pets } } "#, ); } #[test] fn valid_scalar_selection_with_args() { expect_passes_rule( factory, r#" fragment scalarSelectionWithArgs on Dog { doesKnowCommand(dogCommand: SIT) } "#, ); } #[test] fn scalar_selection_not_allowed_on_boolean() { expect_fails_rule( factory, r#" fragment scalarSelectionsNotAllowedOnBoolean on Dog { barks { sinceWhen } } "#, ); } #[test] fn scalar_selection_not_allowed_on_enum() { expect_fails_rule( factory, r#" fragment scalarSelectionsNotAllowedOnEnum on Cat { furColor { inHexdec } } "#, ); } #[test] fn scalar_selection_not_allowed_with_args() { expect_fails_rule( factory, r#" fragment scalarSelectionsNotAllowedWithArgs on Dog { doesKnowCommand(dogCommand: SIT) { sinceWhen } } "#, ); } #[test] fn scalar_selection_not_allowed_with_directives() { expect_fails_rule( factory, r#" fragment scalarSelectionsNotAllowedWithDirectives on Dog { name @include(if: true) { isAlsoHumanName } } "#, ); } #[test] fn scalar_selection_not_allowed_with_directives_and_args() { expect_fails_rule( factory, r#" fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog { doesKnowCommand(dogCommand: SIT) @include(if: true) { sinceWhen } } "#, ); } }