async_graphql/validation/rules/
scalar_leafs.rs

1use crate::{
2    Positioned,
3    parser::types::Field,
4    validation::visitor::{Visitor, VisitorContext},
5};
6
7#[derive(Default)]
8pub struct ScalarLeafs;
9
10impl<'a> Visitor<'a> for ScalarLeafs {
11    fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Positioned<Field>) {
12        if let Some(ty) = ctx.parent_type()
13            && let Some(schema_field) = ty.field_by_name(&field.node.name.node)
14            && let Some(ty) = ctx.registry.concrete_type_by_name(&schema_field.ty)
15        {
16            if ty.is_leaf() && !field.node.selection_set.node.items.is_empty() {
17                ctx.report_error(
18                    vec![field.pos],
19                    format!(
20                        "Field \"{}\" must not have a selection since type \"{}\" has no subfields",
21                        field.node.name,
22                        ty.name()
23                    ),
24                )
25            } else if !ty.is_leaf() && field.node.selection_set.node.items.is_empty() {
26                ctx.report_error(
27                    vec![field.pos],
28                    format!(
29                        "Field \"{}\" of type \"{}\" must have a selection of subfields",
30                        field.node.name,
31                        ty.name()
32                    ),
33                )
34            }
35        }
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    pub fn factory() -> ScalarLeafs {
44        ScalarLeafs
45    }
46
47    #[test]
48    fn valid_scalar_selection() {
49        expect_passes_rule!(
50            factory,
51            r#"
52          fragment scalarSelection on Dog {
53            barks
54          }
55          { __typename }
56        "#,
57        );
58    }
59
60    #[test]
61    fn object_type_missing_selection() {
62        expect_fails_rule!(
63            factory,
64            r#"
65          query directQueryOnObjectWithoutSubFields {
66            human
67          }
68        "#,
69        );
70    }
71
72    #[test]
73    fn interface_type_missing_selection() {
74        expect_fails_rule!(
75            factory,
76            r#"
77          {
78            human { pets }
79          }
80        "#,
81        );
82    }
83
84    #[test]
85    fn valid_scalar_selection_with_args() {
86        expect_passes_rule!(
87            factory,
88            r#"
89          fragment scalarSelectionWithArgs on Dog {
90            doesKnowCommand(dogCommand: SIT)
91          }
92          { __typename }
93        "#,
94        );
95    }
96
97    #[test]
98    fn scalar_selection_not_allowed_on_boolean() {
99        expect_fails_rule!(
100            factory,
101            r#"
102          fragment scalarSelectionsNotAllowedOnBoolean on Dog {
103            barks { sinceWhen }
104          }
105          { __typename }
106        "#,
107        );
108    }
109
110    #[test]
111    fn scalar_selection_not_allowed_on_enum() {
112        expect_fails_rule!(
113            factory,
114            r#"
115          fragment scalarSelectionsNotAllowedOnEnum on Cat {
116            furColor { inHexdec }
117          }
118          { __typename }
119        "#,
120        );
121    }
122
123    #[test]
124    fn scalar_selection_not_allowed_with_args() {
125        expect_fails_rule!(
126            factory,
127            r#"
128          fragment scalarSelectionsNotAllowedWithArgs on Dog {
129            doesKnowCommand(dogCommand: SIT) { sinceWhen }
130          }
131          { __typename }
132        "#,
133        );
134    }
135
136    #[test]
137    fn scalar_selection_not_allowed_with_directives() {
138        expect_fails_rule!(
139            factory,
140            r#"
141          fragment scalarSelectionsNotAllowedWithDirectives on Dog {
142            name @include(if: true) { isAlsoHumanName }
143          }
144          { __typename }
145        "#,
146        );
147    }
148
149    #[test]
150    fn scalar_selection_not_allowed_with_directives_and_args() {
151        expect_fails_rule!(
152            factory,
153            r#"
154          fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog {
155            doesKnowCommand(dogCommand: SIT) @include(if: true) { sinceWhen }
156          }
157          { __typename }
158        "#,
159        );
160    }
161}