This commit is contained in:
sunli 2020-04-06 19:57:21 +08:00
parent efad20d4c4
commit 3ac07990bb
6 changed files with 82 additions and 42 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "async-graphql"
version = "1.7.5"
version = "1.7.6"
authors = ["sunli <scott_s829@163.com>"]
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"

View File

@ -1,6 +1,6 @@
[package]
name = "async-graphql-actix-web"
version = "0.7.5"
version = "0.7.6"
authors = ["sunli <scott_s829@163.com>"]
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"

View File

@ -1,6 +1,6 @@
[package]
name = "async-graphql-derive"
version = "1.7.5"
version = "1.7.6"
authors = ["sunli <scott_s829@163.com>"]
edition = "2018"
description = "Macros for async-graphql"

View File

@ -118,6 +118,7 @@ pub use types::{
Connection, DataSource, EmptyEdgeFields, EmptyMutation, EmptySubscription, QueryOperation,
Upload,
};
pub use validation::ValidationMode;
/// Result type, are actually `anyhow::Result<T>`
pub type Result<T> = std::result::Result<T, Error>;

View File

@ -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<Query, Mutation, Subscription> {
pub(crate) validation_mode: ValidationMode,
pub(crate) query: QueryRoot<Query>,
pub(crate) mutation: Mutation,
pub(crate) subscription: Subscription,
@ -73,6 +74,12 @@ impl<Query: ObjectType, Mutation: ObjectType, Subscription: SubscriptionType>
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<Query, Mutation, Subscription> {
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<impl Stream<Item = serde_json::Value>> {
let document = parse_query(source).map_err(Into::<Error>::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;

View File

@ -18,46 +18,77 @@ pub struct CheckResult {
pub depth: usize,
}
pub fn check_rules(registry: &Registry, doc: &Document) -> Result<CheckResult> {
/// 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<CheckResult> {
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 });
}