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 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}