From 3ac07990bb0a0fa1146f07d8b06eb8d1e07b5cd5 Mon Sep 17 00:00:00 2001 From: sunli Date: Mon, 6 Apr 2020 19:57:21 +0800 Subject: [PATCH] v1.7.6 --- Cargo.toml | 4 +- async-graphql-actix-web/Cargo.toml | 4 +- async-graphql-derive/Cargo.toml | 2 +- src/lib.rs | 1 + src/schema.rs | 14 ++++- src/validation/mod.rs | 99 ++++++++++++++++++++---------- 6 files changed, 82 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f1a6af7d..fb012e95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-graphql" -version = "1.7.5" +version = "1.7.6" authors = ["sunli "] edition = "2018" description = "The GraphQL server library implemented by rust" @@ -18,7 +18,7 @@ default = ["bson", "uuid", "url", "chrono-tz", "validators"] validators = ["regex"] [dependencies] -async-graphql-derive = { path = "async-graphql-derive", version = "1.7.5" } +async-graphql-derive = { path = "async-graphql-derive", version = "1.7.6" } graphql-parser = "=0.2.3" anyhow = "1.0.26" thiserror = "1.0.11" diff --git a/async-graphql-actix-web/Cargo.toml b/async-graphql-actix-web/Cargo.toml index 8348d1e0..0d16e2b4 100644 --- a/async-graphql-actix-web/Cargo.toml +++ b/async-graphql-actix-web/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-graphql-actix-web" -version = "0.7.5" +version = "0.7.6" authors = ["sunli "] edition = "2018" description = "async-graphql for actix-web" @@ -13,7 +13,7 @@ keywords = ["futures", "async", "graphql"] categories = ["network-programming", "asynchronous"] [dependencies] -async-graphql = { path = "..", version = "1.7.5" } +async-graphql = { path = "..", version = "1.7.6" } actix-web = "2.0.0" actix-multipart = "0.2.0" actix-web-actors = "2.0.0" diff --git a/async-graphql-derive/Cargo.toml b/async-graphql-derive/Cargo.toml index 8ffff5c9..2fbd529f 100644 --- a/async-graphql-derive/Cargo.toml +++ b/async-graphql-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-graphql-derive" -version = "1.7.5" +version = "1.7.6" authors = ["sunli "] edition = "2018" description = "Macros for async-graphql" diff --git a/src/lib.rs b/src/lib.rs index ac6d15cc..dabfb1fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,6 +118,7 @@ pub use types::{ Connection, DataSource, EmptyEdgeFields, EmptyMutation, EmptySubscription, QueryOperation, Upload, }; +pub use validation::ValidationMode; /// Result type, are actually `anyhow::Result` pub type Result = std::result::Result; diff --git a/src/schema.rs b/src/schema.rs index 5c7b86f6..feb936f1 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -5,7 +5,7 @@ use crate::query::QueryBuilder; use crate::registry::{Directive, InputValue, Registry}; use crate::subscription::{create_connection, create_subscription_stream, SubscriptionTransport}; use crate::types::QueryRoot; -use crate::validation::{check_rules, CheckResult}; +use crate::validation::{check_rules, CheckResult, ValidationMode}; use crate::{ ContextSelectionSet, Error, ObjectType, Pos, QueryError, QueryResponse, Result, SubscriptionStream, SubscriptionType, Type, Variables, @@ -22,6 +22,7 @@ use std::sync::atomic::AtomicUsize; use std::sync::Arc; pub(crate) struct SchemaInner { + pub(crate) validation_mode: ValidationMode, pub(crate) query: QueryRoot, pub(crate) mutation: Mutation, pub(crate) subscription: Subscription, @@ -73,6 +74,12 @@ impl self } + /// Set the validation mode, default is `ValidationMode::Strict`. + pub fn validation_mode(mut self, validation_mode: ValidationMode) -> Self { + self.0.validation_mode = validation_mode; + self + } + /// Build schema. pub fn finish(self) -> Schema { Schema(Arc::new(self.0)) @@ -180,6 +187,7 @@ where } SchemaBuilder(SchemaInner { + validation_mode: ValidationMode::Strict, query: QueryRoot { inner: query, disable_introspection: false, @@ -230,7 +238,7 @@ where cache_control, complexity, depth, - } = check_rules(&self.0.registry, &document)?; + } = check_rules(&self.0.registry, &document, self.0.validation_mode)?; extensions.iter().for_each(|e| e.validation_end()); if let Some(limit_complexity) = self.0.complexity { @@ -271,7 +279,7 @@ where variables: Variables, ) -> Result> { let document = parse_query(source).map_err(Into::::into)?; - check_rules(&self.0.registry, &document)?; + check_rules(&self.0.registry, &document, self.0.validation_mode)?; let mut fragments = HashMap::new(); let mut subscription = None; diff --git a/src/validation/mod.rs b/src/validation/mod.rs index c5893d5d..b15b6746 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -18,46 +18,77 @@ pub struct CheckResult { pub depth: usize, } -pub fn check_rules(registry: &Registry, doc: &Document) -> Result { +/// Validation mode +#[derive(Copy, Clone, Debug)] +pub enum ValidationMode { + /// Execute all validation rules. + Strict, + + /// The executor itself also has error handling, so it can improve performance, but it can lose some error messages. + Fast, +} + +pub fn check_rules( + registry: &Registry, + doc: &Document, + mode: ValidationMode, +) -> Result { let mut ctx = VisitorContext::new(registry, doc); let mut cache_control = CacheControl::default(); let mut complexity = 0; let mut depth = 0; - let mut visitor = VisitorNil - .with(rules::ArgumentsOfCorrectType::default()) - .with(rules::DefaultValuesOfCorrectType) - .with(rules::FieldsOnCorrectType) - .with(rules::FragmentsOnCompositeTypes) - .with(rules::KnownArgumentNames::default()) - .with(rules::NoFragmentCycles::default()) - .with(rules::KnownFragmentNames) - .with(rules::KnownTypeNames) - .with(rules::LoneAnonymousOperation::default()) - .with(rules::NoUndefinedVariables::default()) - .with(rules::NoUnusedFragments::default()) - .with(rules::NoUnusedVariables::default()) - .with(rules::UniqueArgumentNames::default()) - .with(rules::UniqueFragmentNames::default()) - .with(rules::UniqueOperationNames::default()) - .with(rules::UniqueVariableNames::default()) - .with(rules::VariablesAreInputTypes) - .with(rules::VariableInAllowedPosition::default()) - .with(rules::ScalarLeafs) - .with(rules::PossibleFragmentSpreads::default()) - .with(rules::ProvidedNonNullArguments) - .with(rules::KnownDirectives::default()) - .with(rules::OverlappingFieldsCanBeMerged) - .with(rules::UploadFile) - .with(visitors::CacheControlCalculate { - cache_control: &mut cache_control, - }) - .with(visitors::ComplexityCalculate { - complexity: &mut complexity, - }) - .with(visitors::DepthCalculate::new(&mut depth)); + match mode { + ValidationMode::Strict => { + let mut visitor = VisitorNil + .with(rules::ArgumentsOfCorrectType::default()) + .with(rules::DefaultValuesOfCorrectType) + .with(rules::FieldsOnCorrectType) + .with(rules::FragmentsOnCompositeTypes) + .with(rules::KnownArgumentNames::default()) + .with(rules::NoFragmentCycles::default()) + .with(rules::KnownFragmentNames) + .with(rules::KnownTypeNames) + .with(rules::LoneAnonymousOperation::default()) + .with(rules::NoUndefinedVariables::default()) + .with(rules::NoUnusedFragments::default()) + .with(rules::NoUnusedVariables::default()) + .with(rules::UniqueArgumentNames::default()) + .with(rules::UniqueFragmentNames::default()) + .with(rules::UniqueOperationNames::default()) + .with(rules::UniqueVariableNames::default()) + .with(rules::VariablesAreInputTypes) + .with(rules::VariableInAllowedPosition::default()) + .with(rules::ScalarLeafs) + .with(rules::PossibleFragmentSpreads::default()) + .with(rules::ProvidedNonNullArguments) + .with(rules::KnownDirectives::default()) + .with(rules::OverlappingFieldsCanBeMerged) + .with(rules::UploadFile) + .with(visitors::CacheControlCalculate { + cache_control: &mut cache_control, + }) + .with(visitors::ComplexityCalculate { + complexity: &mut complexity, + }) + .with(visitors::DepthCalculate::new(&mut depth)); + visit(&mut visitor, &mut ctx, doc); + } + ValidationMode::Fast => { + let mut visitor = VisitorNil + .with(rules::NoFragmentCycles::default()) + .with(rules::UploadFile) + .with(visitors::CacheControlCalculate { + cache_control: &mut cache_control, + }) + .with(visitors::ComplexityCalculate { + complexity: &mut complexity, + }) + .with(visitors::DepthCalculate::new(&mut depth)); + visit(&mut visitor, &mut ctx, doc); + } + } - visit(&mut visitor, &mut ctx, doc); if !ctx.errors.is_empty() { return Err(Error::Rule { errors: ctx.errors }); }