async-graphql/src/validation/rules/scalar_leafs.rs

157 lines
3.9 KiB
Rust
Raw Normal View History

2020-09-06 05:38:31 +00:00
use crate::parser::types::Field;
2020-03-24 10:54:22 +00:00
use crate::validation::visitor::{Visitor, VisitorContext};
2020-05-10 02:59:51 +00:00
use crate::Positioned;
2020-03-10 06:14:09 +00:00
#[derive(Default)]
pub struct ScalarLeafs;
impl<'a> Visitor<'a> for ScalarLeafs {
2020-05-10 02:59:51 +00:00
fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Positioned<Field>) {
2020-03-10 06:14:09 +00:00
if let Some(ty) = ctx.parent_type() {
2020-09-06 05:38:31 +00:00
if let Some(schema_field) = ty.field_by_name(&field.node.name.node) {
2020-04-06 10:30:38 +00:00
if let Some(ty) = ctx.registry.concrete_type_by_name(&schema_field.ty) {
2020-09-06 05:38:31 +00:00
if ty.is_leaf() && !field.node.selection_set.node.items.is_empty() {
ctx.report_error(vec![field.pos], format!(
2020-03-10 06:14:09 +00:00
"Field \"{}\" must not have a selection since type \"{}\" has no subfields",
2020-09-06 05:38:31 +00:00
field.node.name, ty.name()
2020-03-10 06:14:09 +00:00
))
2020-09-06 05:38:31 +00:00
} else if !ty.is_leaf() && field.node.selection_set.node.items.is_empty() {
2020-04-05 08:00:26 +00:00
ctx.report_error(
2020-09-06 05:38:31 +00:00
vec![field.pos],
2020-04-05 08:00:26 +00:00
format!(
"Field \"{}\" of type \"{}\" must have a selection of subfields",
2020-09-06 05:38:31 +00:00
field.node.name,
2020-04-05 08:00:26 +00:00
ty.name()
),
)
2020-03-10 06:14:09 +00:00
}
}
}
}
}
}
2020-04-05 08:00:26 +00:00
#[cfg(test)]
mod tests {
use super::*;
pub fn factory() -> ScalarLeafs {
ScalarLeafs
}
#[test]
fn valid_scalar_selection() {
2020-05-29 09:29:15 +00:00
expect_passes_rule!(
2020-04-05 08:00:26 +00:00
factory,
r#"
fragment scalarSelection on Dog {
barks
}
{ __typename }
2020-04-05 08:00:26 +00:00
"#,
);
}
#[test]
fn object_type_missing_selection() {
2020-05-29 09:29:15 +00:00
expect_fails_rule!(
2020-04-05 08:00:26 +00:00
factory,
r#"
query directQueryOnObjectWithoutSubFields {
human
}
"#,
);
}
#[test]
fn interface_type_missing_selection() {
2020-05-29 09:29:15 +00:00
expect_fails_rule!(
2020-04-05 08:00:26 +00:00
factory,
r#"
{
human { pets }
}
"#,
);
}
#[test]
fn valid_scalar_selection_with_args() {
2020-05-29 09:29:15 +00:00
expect_passes_rule!(
2020-04-05 08:00:26 +00:00
factory,
r#"
fragment scalarSelectionWithArgs on Dog {
doesKnowCommand(dogCommand: SIT)
}
{ __typename }
2020-04-05 08:00:26 +00:00
"#,
);
}
#[test]
fn scalar_selection_not_allowed_on_boolean() {
2020-05-29 09:29:15 +00:00
expect_fails_rule!(
2020-04-05 08:00:26 +00:00
factory,
r#"
fragment scalarSelectionsNotAllowedOnBoolean on Dog {
barks { sinceWhen }
}
{ __typename }
2020-04-05 08:00:26 +00:00
"#,
);
}
#[test]
fn scalar_selection_not_allowed_on_enum() {
2020-05-29 09:29:15 +00:00
expect_fails_rule!(
2020-04-05 08:00:26 +00:00
factory,
r#"
fragment scalarSelectionsNotAllowedOnEnum on Cat {
furColor { inHexdec }
}
{ __typename }
2020-04-05 08:00:26 +00:00
"#,
);
}
#[test]
fn scalar_selection_not_allowed_with_args() {
2020-05-29 09:29:15 +00:00
expect_fails_rule!(
2020-04-05 08:00:26 +00:00
factory,
r#"
fragment scalarSelectionsNotAllowedWithArgs on Dog {
doesKnowCommand(dogCommand: SIT) { sinceWhen }
}
{ __typename }
2020-04-05 08:00:26 +00:00
"#,
);
}
#[test]
fn scalar_selection_not_allowed_with_directives() {
2020-05-29 09:29:15 +00:00
expect_fails_rule!(
2020-04-05 08:00:26 +00:00
factory,
r#"
fragment scalarSelectionsNotAllowedWithDirectives on Dog {
name @include(if: true) { isAlsoHumanName }
}
{ __typename }
2020-04-05 08:00:26 +00:00
"#,
);
}
#[test]
fn scalar_selection_not_allowed_with_directives_and_args() {
2020-05-29 09:29:15 +00:00
expect_fails_rule!(
2020-04-05 08:00:26 +00:00
factory,
r#"
fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog {
doesKnowCommand(dogCommand: SIT) @include(if: true) { sinceWhen }
}
{ __typename }
2020-04-05 08:00:26 +00:00
"#,
);
}
}