async_graphql/validation/rules/
scalar_leafs.rs1use 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}