async_graphql/validation/
mod.rs1#[cfg(test)]
2#[macro_use]
3mod test_harness;
4
5mod rules;
6mod suggestion;
7mod utils;
8mod visitor;
9mod visitors;
10
11pub use visitor::VisitorContext;
12use visitor::{visit, VisitorNil};
13
14use crate::{
15 parser::types::ExecutableDocument, registry::Registry, CacheControl, ServerError, Variables,
16};
17
18#[derive(Debug, Copy, Clone)]
20pub struct ValidationResult {
21 pub cache_control: CacheControl,
23
24 pub complexity: usize,
26
27 pub depth: usize,
29}
30
31#[derive(Copy, Clone, Debug)]
33pub enum ValidationMode {
34 Strict,
36
37 Fast,
40}
41
42pub(crate) fn check_rules(
43 registry: &Registry,
44 doc: &ExecutableDocument,
45 variables: Option<&Variables>,
46 mode: ValidationMode,
47 limit_complexity: Option<usize>,
48 limit_depth: Option<usize>,
49) -> Result<ValidationResult, Vec<ServerError>> {
50 let mut cache_control = CacheControl::default();
51 let mut complexity = 0;
52 let mut depth = 0;
53
54 let errors = match mode {
55 ValidationMode::Strict => {
56 let mut ctx = VisitorContext::new(registry, doc, variables);
57 let mut visitor = VisitorNil
58 .with(rules::ArgumentsOfCorrectType::default())
59 .with(rules::DefaultValuesOfCorrectType)
60 .with(rules::FieldsOnCorrectType)
61 .with(rules::FragmentsOnCompositeTypes)
62 .with(rules::KnownArgumentNames::default())
63 .with(rules::NoFragmentCycles::default())
64 .with(rules::KnownFragmentNames)
65 .with(rules::KnownTypeNames)
66 .with(rules::NoUndefinedVariables::default())
67 .with(rules::NoUnusedFragments::default())
68 .with(rules::NoUnusedVariables::default())
69 .with(rules::UniqueArgumentNames::default())
70 .with(rules::UniqueVariableNames::default())
71 .with(rules::VariablesAreInputTypes)
72 .with(rules::VariableInAllowedPosition::default())
73 .with(rules::ScalarLeafs)
74 .with(rules::PossibleFragmentSpreads::default())
75 .with(rules::ProvidedNonNullArguments)
76 .with(rules::KnownDirectives::default())
77 .with(rules::DirectivesUnique)
78 .with(rules::OverlappingFieldsCanBeMerged)
79 .with(rules::UploadFile);
80 visit(&mut visitor, &mut ctx, doc);
81
82 let mut visitor = VisitorNil
83 .with(visitors::CacheControlCalculate {
84 cache_control: &mut cache_control,
85 })
86 .with(visitors::ComplexityCalculate::new(&mut complexity))
87 .with(visitors::DepthCalculate::new(&mut depth));
88 visit(&mut visitor, &mut ctx, doc);
89 ctx.errors
90 }
91 ValidationMode::Fast => {
92 let mut ctx = VisitorContext::new(registry, doc, variables);
93 let mut visitor = VisitorNil
94 .with(rules::NoFragmentCycles::default())
95 .with(rules::UploadFile)
96 .with(visitors::CacheControlCalculate {
97 cache_control: &mut cache_control,
98 })
99 .with(visitors::ComplexityCalculate::new(&mut complexity))
100 .with(visitors::DepthCalculate::new(&mut depth));
101 visit(&mut visitor, &mut ctx, doc);
102 ctx.errors
103 }
104 };
105
106 if let Some(limit_complexity) = limit_complexity {
108 if complexity > limit_complexity {
109 return Err(vec![ServerError::new("Query is too complex.", None)]);
110 }
111 }
112
113 if let Some(limit_depth) = limit_depth {
114 if depth > limit_depth {
115 return Err(vec![ServerError::new("Query is nested too deep.", None)]);
116 }
117 }
118
119 if !errors.is_empty() {
120 return Err(errors.into_iter().map(Into::into).collect());
121 }
122
123 Ok(ValidationResult {
124 cache_control,
125 complexity,
126 depth,
127 })
128}