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