async_graphql/validation/
mod.rs

1#[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/// Validation results.
19#[derive(Debug, Copy, Clone)]
20pub struct ValidationResult {
21    /// Cache control
22    pub cache_control: CacheControl,
23
24    /// Query complexity
25    pub complexity: usize,
26
27    /// Query depth
28    pub depth: usize,
29}
30
31/// Validation mode
32#[derive(Copy, Clone, Debug)]
33pub enum ValidationMode {
34    /// Execute all validation rules.
35    Strict,
36
37    /// The executor itself also has error handling, so it can improve
38    /// performance, but it can lose some error messages.
39    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    // check limit
107    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}