v0.10.2
This commit is contained in:
parent
0169fd6122
commit
3de914df7a
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "async-graphql"
|
||||
version = "0.10.1"
|
||||
version = "0.10.2"
|
||||
authors = ["sunli <scott_s829@163.com>"]
|
||||
edition = "2018"
|
||||
description = "The GraphQL server library implemented by rust"
|
||||
|
@ -17,7 +17,7 @@ readme = "README.md"
|
|||
default = ["chrono", "uuid"]
|
||||
|
||||
[dependencies]
|
||||
async-graphql-derive = { path = "async-graphql-derive", version = "0.10.1" }
|
||||
async-graphql-derive = { path = "async-graphql-derive", version = "0.10.2" }
|
||||
graphql-parser = "0.2.3"
|
||||
anyhow = "1.0.26"
|
||||
thiserror = "1.0.11"
|
||||
|
|
10
README.md
10
README.md
|
@ -85,14 +85,14 @@
|
|||
- [ ] Validation rules
|
||||
- [X] ArgumentsOfCorrectType
|
||||
- [X] DefaultValuesOfCorrectType
|
||||
- [ ] FieldsOnCorrectType
|
||||
- [ ] FragmentsOnCompositeTypes
|
||||
- [ ] KnownArgumentNames
|
||||
- [X] FieldsOnCorrectType
|
||||
- [X] FragmentsOnCompositeTypes
|
||||
- [X] KnownArgumentNames
|
||||
- [ ] KnownDirectives
|
||||
- [ ] KnownFragmentNames
|
||||
- [X] KnownFragmentNames
|
||||
- [ ] KnownTypeNames
|
||||
- [ ] LoneAnonymousOperation
|
||||
- [ ] NoFragmentCycles
|
||||
- [X] NoFragmentCycles
|
||||
- [ ] NoUndefinedVariables
|
||||
- [ ] NoUnusedFragments
|
||||
- [ ] NoUnusedVariables
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "async-graphql-derive"
|
||||
version = "0.10.1"
|
||||
version = "0.10.2"
|
||||
authors = ["sunli <scott_s829@163.com>"]
|
||||
edition = "2018"
|
||||
description = "The GraphQL server library implemented by rust"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::model::{__EnumValue, __Field, __InputValue, __TypeKind};
|
||||
use crate::registry;
|
||||
use crate::registry::{Type, TypeInfo};
|
||||
use crate::registry::{Type, TypeName};
|
||||
use async_graphql_derive::Object;
|
||||
|
||||
enum TypeDetail<'a> {
|
||||
|
@ -23,16 +23,16 @@ impl<'a> __Type<'a> {
|
|||
}
|
||||
|
||||
pub fn new(registry: &'a registry::Registry, type_name: &str) -> __Type<'a> {
|
||||
match TypeInfo::create(type_name) {
|
||||
TypeInfo::NonNull(ty) => __Type {
|
||||
match TypeName::create(type_name) {
|
||||
TypeName::NonNull(ty) => __Type {
|
||||
registry,
|
||||
detail: TypeDetail::NonNull(ty.to_string()),
|
||||
},
|
||||
TypeInfo::List(ty) => __Type {
|
||||
TypeName::List(ty) => __Type {
|
||||
registry,
|
||||
detail: TypeDetail::List(ty.to_string()),
|
||||
},
|
||||
TypeInfo::Type(ty) => __Type {
|
||||
TypeName::Name(ty) => __Type {
|
||||
registry,
|
||||
detail: TypeDetail::Simple(®istry.types[ty]),
|
||||
},
|
||||
|
|
|
@ -17,20 +17,20 @@ fn parse_list(type_name: &str) -> Option<&str> {
|
|||
}
|
||||
}
|
||||
|
||||
pub enum TypeInfo<'a> {
|
||||
pub enum TypeName<'a> {
|
||||
List(&'a str),
|
||||
NonNull(&'a str),
|
||||
Type(&'a str),
|
||||
Name(&'a str),
|
||||
}
|
||||
|
||||
impl<'a> TypeInfo<'a> {
|
||||
pub fn create(type_name: &str) -> TypeInfo {
|
||||
impl<'a> TypeName<'a> {
|
||||
pub fn create(type_name: &str) -> TypeName {
|
||||
if let Some(type_name) = parse_non_null(type_name) {
|
||||
TypeInfo::NonNull(type_name)
|
||||
TypeName::NonNull(type_name)
|
||||
} else if let Some(type_name) = parse_list(type_name) {
|
||||
TypeInfo::List(type_name)
|
||||
TypeName::List(type_name)
|
||||
} else {
|
||||
TypeInfo::Type(type_name)
|
||||
TypeName::Name(type_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,6 +113,23 @@ impl Type {
|
|||
Type::InputObject { name, .. } => name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_composite(&self) -> bool {
|
||||
match self {
|
||||
Type::Object { .. } => true,
|
||||
Type::Interface { .. } => true,
|
||||
Type::Union { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_leaf(&self) -> bool {
|
||||
match self {
|
||||
Type::Enum { .. } => true,
|
||||
Type::Scalar { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Directive {
|
||||
|
@ -147,7 +164,7 @@ impl Registry {
|
|||
fields.insert(
|
||||
"__typename",
|
||||
Field {
|
||||
name: "",
|
||||
name: "__typename",
|
||||
description: None,
|
||||
args: Default::default(),
|
||||
ty: "String!".to_string(),
|
||||
|
@ -179,10 +196,10 @@ impl Registry {
|
|||
}
|
||||
|
||||
pub fn get_basic_type(&self, type_name: &str) -> Option<&Type> {
|
||||
match TypeInfo::create(type_name) {
|
||||
TypeInfo::Type(type_name) => self.types.get(type_name),
|
||||
TypeInfo::List(type_name) => self.get_basic_type(type_name),
|
||||
TypeInfo::NonNull(type_name) => self.get_basic_type(type_name),
|
||||
match TypeName::create(type_name) {
|
||||
TypeName::Name(type_name) => self.types.get(type_name),
|
||||
TypeName::List(type_name) => self.get_basic_type(type_name),
|
||||
TypeName::NonNull(type_name) => self.get_basic_type(type_name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,30 @@
|
|||
use crate::error::RuleError;
|
||||
use crate::registry::{Registry, Type};
|
||||
use graphql_parser::query::{Definition, Document};
|
||||
use graphql_parser::Pos;
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub struct ValidatorContext<'a> {
|
||||
pub registry: &'a Registry,
|
||||
pub errors: Vec<RuleError>,
|
||||
type_stack: Vec<&'a Type>,
|
||||
fragments_names: HashSet<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> ValidatorContext<'a> {
|
||||
pub fn new(registry: &'a Registry) -> Self {
|
||||
pub fn new(registry: &'a Registry, doc: &'a Document) -> Self {
|
||||
Self {
|
||||
registry,
|
||||
errors: Default::default(),
|
||||
type_stack: Default::default(),
|
||||
fragments_names: doc
|
||||
.definitions
|
||||
.iter()
|
||||
.filter_map(|d| match d {
|
||||
Definition::Fragment(fragment) => Some(fragment.name.as_str()),
|
||||
_ => None,
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,13 +35,25 @@ impl<'a> ValidatorContext<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn append_errors(&mut self, errors: Vec<RuleError>) {
|
||||
self.errors.extend(errors);
|
||||
}
|
||||
|
||||
pub fn with_type<F: FnMut(&mut ValidatorContext<'a>)>(&mut self, ty: &'a Type, mut f: F) {
|
||||
self.type_stack.push(ty);
|
||||
f(self);
|
||||
self.type_stack.pop();
|
||||
}
|
||||
|
||||
pub fn parent_type(&self) -> &'a Type {
|
||||
pub fn parent_type(&self) -> Option<&'a Type> {
|
||||
self.type_stack.get(self.type_stack.len() - 2).map(|t| *t)
|
||||
}
|
||||
|
||||
pub fn current_type(&self) -> &'a Type {
|
||||
self.type_stack.last().unwrap()
|
||||
}
|
||||
|
||||
pub fn is_known_fragment(&self, name: &str) -> bool {
|
||||
self.fragments_names.contains(name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,15 @@ mod utils;
|
|||
mod visitor;
|
||||
|
||||
pub fn check_rules(registry: &Registry, doc: &Document) -> Result<()> {
|
||||
let mut ctx = ValidatorContext::new(registry);
|
||||
let mut ctx = ValidatorContext::new(registry, doc);
|
||||
let mut visitor = VisitorNil
|
||||
.with(rules::ArgumentsOfCorrectType::default())
|
||||
.with(rules::DefaultValuesOfCorrectType);
|
||||
.with(rules::DefaultValuesOfCorrectType)
|
||||
.with(rules::FieldsOnCorrectType)
|
||||
.with(rules::FragmentsOnCompositeTypes)
|
||||
.with(rules::KnownArgumentNames::default())
|
||||
.with(rules::NoFragmentCycles::default())
|
||||
.with(rules::KnownFragmentNames);
|
||||
|
||||
visit(&mut visitor, &mut ctx, doc);
|
||||
if !ctx.errors.is_empty() {
|
||||
|
|
|
@ -45,7 +45,7 @@ impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> {
|
|||
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Field) {
|
||||
self.current_args = ctx
|
||||
.parent_type()
|
||||
.field_by_name(&field.name)
|
||||
.and_then(|p| p.field_by_name(&field.name))
|
||||
.map(|f| (&f.args, field.position));
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ use crate::validation::utils::is_valid_input_value;
|
|||
use crate::validation::visitor::Visitor;
|
||||
use graphql_parser::query::{Type, VariableDefinition};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DefaultValuesOfCorrectType;
|
||||
|
||||
impl<'a> Visitor<'a> for DefaultValuesOfCorrectType {
|
||||
|
@ -15,7 +14,7 @@ impl<'a> Visitor<'a> for DefaultValuesOfCorrectType {
|
|||
if let Some(value) = &variable_definition.default_value {
|
||||
if let Type::NonNullType(_) = variable_definition.var_type {
|
||||
ctx.report_error(vec![variable_definition.position],format!(
|
||||
"Argument \"{}\" has type \"{}\" and is not nullable, so it't can't have a default value",
|
||||
"Argument \"#{}\" has type \"{}\" and is not nullable, so it't can't have a default value",
|
||||
variable_definition.name, variable_definition.var_type,
|
||||
));
|
||||
} else {
|
||||
|
|
33
src/validation/rules/fields_on_correct_type.rs
Normal file
33
src/validation/rules/fields_on_correct_type.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use crate::registry::Type;
|
||||
use crate::validation::context::ValidatorContext;
|
||||
use crate::validation::visitor::Visitor;
|
||||
use graphql_parser::query::Field;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FieldsOnCorrectType;
|
||||
|
||||
impl<'a> Visitor<'a> for FieldsOnCorrectType {
|
||||
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Field) {
|
||||
if ctx
|
||||
.parent_type()
|
||||
.unwrap()
|
||||
.field_by_name(&field.name)
|
||||
.is_none()
|
||||
{
|
||||
if let Some(Type::Union { .. }) = ctx.parent_type() {
|
||||
if field.name == "__typename" {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.report_error(
|
||||
vec![field.position],
|
||||
format!(
|
||||
"Unknown field \"{}\" on type \"{}\"",
|
||||
field.name,
|
||||
ctx.parent_type().unwrap().name()
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
41
src/validation/rules/fragments_on_composite_types.rs
Normal file
41
src/validation/rules/fragments_on_composite_types.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use crate::validation::context::ValidatorContext;
|
||||
use crate::validation::visitor::Visitor;
|
||||
use graphql_parser::query::{FragmentDefinition, InlineFragment, TypeCondition};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FragmentsOnCompositeTypes;
|
||||
|
||||
impl<'a> Visitor<'a> for FragmentsOnCompositeTypes {
|
||||
fn enter_fragment_definition(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
fragment_definition: &'a FragmentDefinition,
|
||||
) {
|
||||
if !ctx.current_type().is_composite() {
|
||||
let TypeCondition::On(name) = &fragment_definition.type_condition;
|
||||
ctx.report_error(
|
||||
vec![fragment_definition.position],
|
||||
format!(
|
||||
"Fragment \"{}\" cannot condition non composite type \"{}\"",
|
||||
fragment_definition.name, name
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_inline_fragment(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
inline_fragment: &'a InlineFragment,
|
||||
) {
|
||||
if !ctx.current_type().is_composite() {
|
||||
ctx.report_error(
|
||||
vec![inline_fragment.position],
|
||||
format!(
|
||||
"Fragment cannot condition non composite type \"{}\"",
|
||||
ctx.current_type().name()
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
86
src/validation/rules/known_argument_names.rs
Normal file
86
src/validation/rules/known_argument_names.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
use crate::registry::InputValue;
|
||||
use crate::validation::context::ValidatorContext;
|
||||
use crate::validation::visitor::Visitor;
|
||||
use crate::Value;
|
||||
use graphql_parser::query::{Directive, Field};
|
||||
use graphql_parser::Pos;
|
||||
use std::collections::HashMap;
|
||||
|
||||
enum ArgsType<'a> {
|
||||
Directive(&'a str),
|
||||
Field {
|
||||
field_name: &'a str,
|
||||
type_name: &'a str,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct KnownArgumentNames<'a> {
|
||||
current_args: Option<(&'a HashMap<&'static str, InputValue>, ArgsType<'a>, Pos)>,
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for KnownArgumentNames<'a> {
|
||||
fn enter_directive(&mut self, ctx: &mut ValidatorContext<'a>, directive: &'a Directive) {
|
||||
self.current_args = ctx.registry.directives.get(&directive.name).map(|d| {
|
||||
(
|
||||
&d.args,
|
||||
ArgsType::Directive(&directive.name),
|
||||
directive.position,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
fn exit_directive(&mut self, _ctx: &mut ValidatorContext<'a>, _directive: &'a Directive) {
|
||||
self.current_args = None;
|
||||
}
|
||||
|
||||
fn enter_argument(&mut self, ctx: &mut ValidatorContext<'a>, name: &str, _value: &'a Value) {
|
||||
if let Some((args, arg_type, pos)) = &self.current_args {
|
||||
if !args.contains_key(name) {
|
||||
match arg_type {
|
||||
ArgsType::Field {
|
||||
field_name,
|
||||
type_name,
|
||||
} => {
|
||||
ctx.report_error(
|
||||
vec![*pos],
|
||||
format!(
|
||||
"Unknown argument \"{}\" on field \"{}\" of type \"{}\"",
|
||||
name, field_name, type_name,
|
||||
),
|
||||
);
|
||||
}
|
||||
ArgsType::Directive(directive_name) => {
|
||||
ctx.report_error(
|
||||
vec![*pos],
|
||||
format!(
|
||||
"Unknown argument \"{}\" on directive \"{}\"",
|
||||
name, directive_name
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Field) {
|
||||
self.current_args = ctx
|
||||
.parent_type()
|
||||
.and_then(|p| p.field_by_name(&field.name))
|
||||
.map(|f| {
|
||||
(
|
||||
&f.args,
|
||||
ArgsType::Field {
|
||||
field_name: &field.name,
|
||||
type_name: ctx.parent_type().unwrap().name(),
|
||||
},
|
||||
field.position,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
fn exit_field(&mut self, _ctx: &mut ValidatorContext<'a>, _field: &'a Field) {
|
||||
self.current_args = None;
|
||||
}
|
||||
}
|
21
src/validation/rules/known_fragment_names.rs
Normal file
21
src/validation/rules/known_fragment_names.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use crate::validation::context::ValidatorContext;
|
||||
use crate::validation::visitor::Visitor;
|
||||
use graphql_parser::query::FragmentSpread;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct KnownFragmentNames;
|
||||
|
||||
impl<'a> Visitor<'a> for KnownFragmentNames {
|
||||
fn enter_fragment_spread(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
fragment_spread: &'a FragmentSpread,
|
||||
) {
|
||||
if !ctx.is_known_fragment(&fragment_spread.fragment_name) {
|
||||
ctx.report_error(
|
||||
vec![fragment_spread.position],
|
||||
format!(r#"Unknown fragment: "{}""#, fragment_spread.fragment_name),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,15 @@
|
|||
mod arguments_of_correct_type;
|
||||
mod default_values_of_correct_type;
|
||||
mod fields_on_correct_type;
|
||||
mod fragments_on_composite_types;
|
||||
mod known_argument_names;
|
||||
mod known_fragment_names;
|
||||
mod no_fragment_cycles;
|
||||
|
||||
pub use arguments_of_correct_type::ArgumentsOfCorrectType;
|
||||
pub use default_values_of_correct_type::DefaultValuesOfCorrectType;
|
||||
pub use fields_on_correct_type::FieldsOnCorrectType;
|
||||
pub use fragments_on_composite_types::FragmentsOnCompositeTypes;
|
||||
pub use known_argument_names::KnownArgumentNames;
|
||||
pub use known_fragment_names::KnownFragmentNames;
|
||||
pub use no_fragment_cycles::NoFragmentCycles;
|
||||
|
|
105
src/validation/rules/no_fragment_cycles.rs
Normal file
105
src/validation/rules/no_fragment_cycles.rs
Normal file
|
@ -0,0 +1,105 @@
|
|||
use crate::error::RuleError;
|
||||
use crate::validation::context::ValidatorContext;
|
||||
use crate::validation::visitor::Visitor;
|
||||
use graphql_parser::query::{Document, FragmentDefinition, FragmentSpread};
|
||||
use graphql_parser::Pos;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
struct CycleDetector<'a> {
|
||||
visited: HashSet<&'a str>,
|
||||
spreads: &'a HashMap<&'a str, Vec<(&'a str, Pos)>>,
|
||||
path_indices: HashMap<&'a str, usize>,
|
||||
errors: Vec<RuleError>,
|
||||
}
|
||||
|
||||
impl<'a> CycleDetector<'a> {
|
||||
fn detect_from(&mut self, from: &'a str, path: &mut Vec<(&'a str, Pos)>) {
|
||||
self.visited.insert(from);
|
||||
|
||||
if !self.spreads.contains_key(from) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.path_indices.insert(from, path.len());
|
||||
|
||||
for (name, pos) in &self.spreads[from] {
|
||||
let index = self.path_indices.get(name).cloned();
|
||||
|
||||
if let Some(index) = index {
|
||||
let err_pos = if index < path.len() {
|
||||
path[index].1
|
||||
} else {
|
||||
*pos
|
||||
};
|
||||
|
||||
self.errors.push(RuleError {
|
||||
locations: vec![err_pos],
|
||||
message: format!("Cannot spread fragment \"{}\"", name),
|
||||
});
|
||||
} else if !self.visited.contains(name) {
|
||||
path.push((name, *pos));
|
||||
self.detect_from(name, path);
|
||||
path.pop();
|
||||
}
|
||||
}
|
||||
|
||||
self.path_indices.remove(from);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct NoFragmentCycles<'a> {
|
||||
current_fragment: Option<&'a str>,
|
||||
spreads: HashMap<&'a str, Vec<(&'a str, Pos)>>,
|
||||
fragment_order: Vec<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for NoFragmentCycles<'a> {
|
||||
fn exit_document(&mut self, ctx: &mut ValidatorContext<'a>, _doc: &'a Document) {
|
||||
let mut detector = CycleDetector {
|
||||
visited: HashSet::new(),
|
||||
spreads: &self.spreads,
|
||||
path_indices: HashMap::new(),
|
||||
errors: Vec::new(),
|
||||
};
|
||||
|
||||
for frag in &self.fragment_order {
|
||||
if !detector.visited.contains(frag) {
|
||||
let mut path = Vec::new();
|
||||
detector.detect_from(frag, &mut path);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.append_errors(detector.errors);
|
||||
}
|
||||
|
||||
fn enter_fragment_definition(
|
||||
&mut self,
|
||||
_ctx: &mut ValidatorContext<'a>,
|
||||
fragment_definition: &'a FragmentDefinition,
|
||||
) {
|
||||
self.current_fragment = Some(&fragment_definition.name);
|
||||
self.fragment_order.push(&fragment_definition.name);
|
||||
}
|
||||
|
||||
fn exit_fragment_definition(
|
||||
&mut self,
|
||||
_ctx: &mut ValidatorContext<'a>,
|
||||
_fragment_definition: &'a FragmentDefinition,
|
||||
) {
|
||||
self.current_fragment = None;
|
||||
}
|
||||
|
||||
fn enter_fragment_spread(
|
||||
&mut self,
|
||||
_ctx: &mut ValidatorContext<'a>,
|
||||
fragment_spread: &'a FragmentSpread,
|
||||
) {
|
||||
if let Some(current_fragment) = self.current_fragment {
|
||||
self.spreads
|
||||
.entry(current_fragment)
|
||||
.or_insert_with(Vec::new)
|
||||
.push((&fragment_spread.fragment_name, fragment_spread.position));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +1,19 @@
|
|||
use crate::registry::{Registry, Type, TypeInfo};
|
||||
use crate::registry::{Registry, Type, TypeName};
|
||||
use crate::Value;
|
||||
|
||||
pub fn is_valid_input_value(registry: &Registry, type_name: &str, value: &Value) -> bool {
|
||||
match TypeInfo::create(type_name) {
|
||||
TypeInfo::NonNull(type_name) => match value {
|
||||
match TypeName::create(type_name) {
|
||||
TypeName::NonNull(type_name) => match value {
|
||||
Value::Null => false,
|
||||
_ => is_valid_input_value(registry, type_name, value),
|
||||
},
|
||||
TypeInfo::List(type_name) => match value {
|
||||
TypeName::List(type_name) => match value {
|
||||
Value::List(elems) => elems
|
||||
.iter()
|
||||
.all(|elem| is_valid_input_value(registry, type_name, elem)),
|
||||
_ => false,
|
||||
},
|
||||
TypeInfo::Type(type_name) => {
|
||||
TypeName::Name(type_name) => {
|
||||
if let Value::Null = value {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -341,12 +341,39 @@ fn visit_selection<'a, V: Visitor<'a>>(
|
|||
) {
|
||||
v.enter_selection(ctx, selection);
|
||||
match selection {
|
||||
Selection::Field(field) => visit_field(v, ctx, field),
|
||||
Selection::Field(field) => {
|
||||
if let Some(schema_field) = ctx.current_type().field_by_name(&field.name) {
|
||||
ctx.with_type(
|
||||
ctx.registry.get_basic_type(&schema_field.ty).unwrap(),
|
||||
|ctx| {
|
||||
visit_field(v, ctx, field);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
ctx.report_error(
|
||||
vec![field.position],
|
||||
format!(
|
||||
"Cannot query field \"{}\" on type \"{}\".",
|
||||
field.name,
|
||||
ctx.current_type().name()
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Selection::FragmentSpread(fragment_spread) => {
|
||||
visit_fragment_spread(v, ctx, fragment_spread)
|
||||
}
|
||||
Selection::InlineFragment(inline_fragment) => {
|
||||
visit_inline_fragment(v, ctx, inline_fragment)
|
||||
if let Some(TypeCondition::On(name)) = &inline_fragment.type_condition {
|
||||
if let Some(ty) = ctx.registry.types.get(name) {
|
||||
ctx.with_type(ty, |ctx| visit_inline_fragment(v, ctx, inline_fragment));
|
||||
} else {
|
||||
ctx.report_error(
|
||||
vec![inline_fragment.position],
|
||||
format!("Unknown type \"{}\".", name),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
v.exit_selection(ctx, selection);
|
||||
|
@ -356,25 +383,7 @@ fn visit_field<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, fi
|
|||
v.enter_field(ctx, field);
|
||||
visit_arguments(v, ctx, &field.arguments);
|
||||
visit_directives(v, ctx, &field.directives);
|
||||
|
||||
if let Some(schema_field) = ctx.parent_type().field_by_name(&field.name) {
|
||||
ctx.with_type(
|
||||
ctx.registry.get_basic_type(&schema_field.ty).unwrap(),
|
||||
|ctx| {
|
||||
visit_selection_set(v, ctx, &field.selection_set);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
ctx.report_error(
|
||||
vec![field.position],
|
||||
format!(
|
||||
"Cannot query field \"{}\" on type \"{}\".",
|
||||
field.name,
|
||||
ctx.parent_type().name()
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
v.exit_field(ctx, field);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user