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::{VisitorNil, visit};
13
14use crate::{
15 CacheControl, ServerError, Variables, parser::types::ExecutableDocument, registry::Registry,
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 operation_name: Option<&str>,
47 mode: ValidationMode,
48 limit_complexity: Option<usize>,
49 limit_depth: Option<usize>,
50) -> Result<ValidationResult, Vec<ServerError>> {
51 let mut cache_control = CacheControl::default();
52 let mut complexity = 0;
53 let mut depth = 0;
54
55 let mut ctx = VisitorContext::new(registry, doc, variables, operation_name);
56 let errors = match mode {
57 ValidationMode::Strict => {
58 let mut visitor = VisitorNil
59 .with(rules::ArgumentsOfCorrectType::default())
60 .with(rules::DefaultValuesOfCorrectType)
61 .with(rules::FieldsOnCorrectType)
62 .with(rules::FragmentsOnCompositeTypes)
63 .with(rules::KnownArgumentNames::default())
64 .with(rules::NoFragmentCycles::default())
65 .with(rules::KnownFragmentNames)
66 .with(rules::KnownTypeNames)
67 .with(rules::NoUndefinedVariables::default())
68 .with(rules::NoUnusedFragments::default())
69 .with(rules::NoUnusedVariables::default())
70 .with(rules::UniqueArgumentNames::default())
71 .with(rules::UniqueVariableNames::default())
72 .with(rules::VariablesAreInputTypes)
73 .with(rules::VariableInAllowedPosition::default())
74 .with(rules::ScalarLeafs)
75 .with(rules::PossibleFragmentSpreads::default())
76 .with(rules::ProvidedNonNullArguments)
77 .with(rules::KnownDirectives::default())
78 .with(rules::DirectivesUnique)
79 .with(rules::OverlappingFieldsCanBeMerged)
80 .with(rules::UploadFile);
81 visit(&mut visitor, &mut ctx, doc);
82
83 let mut visitor = VisitorNil
84 .with(visitors::CacheControlCalculate {
85 cache_control: &mut cache_control,
86 })
87 .with(visitors::ComplexityCalculate::new(&mut complexity))
88 .with(visitors::DepthCalculate::new(&mut depth));
89 visit(&mut visitor, &mut ctx, doc);
90 ctx.errors
91 }
92 ValidationMode::Fast => {
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 && complexity > limit_complexity
109 {
110 return Err(vec![ServerError::new("Query is too complex.", None)]);
111 }
112
113 if let Some(limit_depth) = limit_depth
114 && depth > limit_depth
115 {
116 return Err(vec![ServerError::new("Query is nested too deep.", None)]);
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}