test_shisho_policy_sdk/gqlgen/query/
validation.rs1use super::{full_path_prefix, BoundQuery, Query, QueryValidationError, Selection, SelectionId};
2use crate::gqlgen::schema::TypeId;
3
4pub(super) fn validate_typename_presence(
5 query: &BoundQuery<'_>,
6) -> Result<(), QueryValidationError> {
7 for fragment in query.query.fragments.iter() {
8 let type_id = match fragment.on {
9 id @ TypeId::Interface(_) | id @ TypeId::Union(_) => id,
10 _ => continue,
11 };
12
13 if !selection_set_contains_type_name(fragment.on, &fragment.selection_set, query.query) {
14 return Err(QueryValidationError::new(format!(
15 "The `{}` fragment uses `{}` but does not select `__typename` on it. graphql-client cannot generate code for it. Please add `__typename` to the selection.",
16 &fragment.name,
17 type_id.name(query.schema),
18 )));
19 }
20 }
21
22 let union_and_interface_field_selections =
23 query
24 .query
25 .selections()
26 .filter_map(|(selection_id, selection)| match selection {
27 Selection::Field(field) => match query.schema.get_field(field.field_id).r#type.id {
28 id @ TypeId::Interface(_) | id @ TypeId::Union(_) => {
29 Some((selection_id, id, &field.selection_set))
30 }
31 _ => None,
32 },
33 _ => None,
34 });
35
36 for selection in union_and_interface_field_selections {
37 if !selection_set_contains_type_name(selection.1, selection.2, query.query) {
38 return Err(QueryValidationError::new(format!(
39 "The query uses `{path}` at `{selected_type}` but does not select `__typename` on it. graphql-client cannot generate code for it. Please add `__typename` to the selection.",
40 path = full_path_prefix(selection.0, query),
41 selected_type = selection.1.name(query.schema)
42 )));
43 }
44 }
45
46 Ok(())
47}
48
49fn selection_set_contains_type_name(
50 parent_type_id: TypeId,
51 selection_set: &[SelectionId],
52 query: &Query,
53) -> bool {
54 for id in selection_set {
55 let selection = query.get_selection(*id);
56
57 match selection {
58 Selection::Typename => return true,
59 Selection::FragmentSpread(fragment_id) => {
60 let fragment = query.get_fragment(*fragment_id);
61 if fragment.on == parent_type_id
62 && selection_set_contains_type_name(fragment.on, &fragment.selection_set, query)
63 {
64 return true;
65 }
66 }
67 _ => (),
68 }
69 }
70
71 false
72}