`#[grapql(validator(list))]` no longer applies to `max_items` and `min_items`.

This commit is contained in:
Sunli 2021-11-30 09:35:54 +08:00
parent 05907cad31
commit 4a265ed29d
3 changed files with 151 additions and 60 deletions

View File

@ -4,7 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.0.7] 2021-11-23
## [3.0.8] 2021-11-30
- `#[graphql(validator(list))]` no longer applies to `max_items` and `min_items`.
- Implement `InputValue`/`OutputValue` for `serde_json::Value`.
## [3.0.7] 2021-11-23
- Fix error extensions cause stack overflow. [#719](https://github.com/async-graphql/async-graphql/issues/719)

View File

@ -71,115 +71,143 @@ impl Validators {
ty: TokenStream,
map_err: Option<TokenStream>,
) -> Result<TokenStream> {
let mut list_validators = Vec::new();
let mut elem_validators = Vec::new();
let mut codes = Vec::new();
if let Some(n) = &self.multiple_of {
codes.push(quote! {
#crate_name::validators::multiple_of(__raw_value, #n)
});
}
if let Some(n) = &self.maximum {
codes.push(quote! {
#crate_name::validators::maximum(__raw_value, #n)
});
}
if let Some(n) = &self.minimum {
codes.push(quote! {
#crate_name::validators::minimum(__raw_value, #n)
});
}
if let Some(n) = &self.max_length {
codes.push(quote! {
#crate_name::validators::max_length(__raw_value, #n)
});
}
if let Some(n) = &self.min_length {
codes.push(quote! {
#crate_name::validators::min_length(__raw_value, #n)
});
}
if let Some(n) = &self.max_items {
codes.push(quote! {
list_validators.push(quote! {
#crate_name::validators::max_items(__raw_value, #n)
});
}
if let Some(n) = &self.min_items {
codes.push(quote! {
list_validators.push(quote! {
#crate_name::validators::min_items(__raw_value, #n)
});
}
if let Some(n) = &self.multiple_of {
elem_validators.push(quote! {
#crate_name::validators::multiple_of(__raw_value, #n)
});
}
if let Some(n) = &self.maximum {
elem_validators.push(quote! {
#crate_name::validators::maximum(__raw_value, #n)
});
}
if let Some(n) = &self.minimum {
elem_validators.push(quote! {
#crate_name::validators::minimum(__raw_value, #n)
});
}
if let Some(n) = &self.max_length {
elem_validators.push(quote! {
#crate_name::validators::max_length(__raw_value, #n)
});
}
if let Some(n) = &self.min_length {
elem_validators.push(quote! {
#crate_name::validators::min_length(__raw_value, #n)
});
}
if let Some(n) = &self.chars_max_length {
codes.push(quote! {
elem_validators.push(quote! {
#crate_name::validators::chars_max_length(__raw_value, #n)
});
}
if let Some(n) = &self.chars_min_length {
codes.push(quote! {
elem_validators.push(quote! {
#crate_name::validators::chars_min_length(__raw_value, #n)
});
}
if self.email {
codes.push(quote! {
elem_validators.push(quote! {
#crate_name::validators::email(__raw_value)
});
}
if self.url {
codes.push(quote! {
elem_validators.push(quote! {
#crate_name::validators::url(__raw_value)
});
}
if self.ip {
codes.push(quote! {
elem_validators.push(quote! {
#crate_name::validators::ip(__raw_value)
});
}
if let Some(re) = &self.regex {
codes.push(quote! {
elem_validators.push(quote! {
#crate_name::validators::regex(__raw_value, #re)
});
}
if !list_validators.is_empty() {
codes.push(quote! {
if let ::std::option::Option::Some(__raw_value) = #crate_name::InputType::as_raw_value(#value) {
#(#list_validators #map_err ?;)*
}
});
}
if !elem_validators.is_empty() {
if self.list {
codes.push(quote! {
for __item in #value {
if let ::std::option::Option::Some(__raw_value) = #crate_name::InputType::as_raw_value(__item) {
#(#elem_validators #map_err ?;)*
}
}
});
} else {
codes.push(quote! {
if let ::std::option::Option::Some(__raw_value) = #crate_name::InputType::as_raw_value(#value) {
#(#elem_validators #map_err ?;)*
}
});
}
}
for s in &self.custom {
let __create_custom_validator: Expr =
syn::parse_str(s).map_err(|err| Error::new(s.span(), err.to_string()))?;
codes.push(quote! {
#crate_name::CustomValidator::check(&(#__create_custom_validator), __raw_value)
.map_err(|err_msg| #crate_name::InputValueError::<#ty>::custom(err_msg))
});
if self.list {
codes.push(quote! {
let __custom_validator = #__create_custom_validator;
for __item in #value {
if let ::std::option::Option::Some(__raw_value) = #crate_name::InputType::as_raw_value(__item) {
#crate_name::CustomValidator::check(&__custom_validator, __raw_value)
.map_err(|err_msg| #crate_name::InputValueError::<#ty>::custom(err_msg)) #map_err ?;
}
}
});
} else {
codes.push(quote! {
let __custom_validator = #__create_custom_validator;
if let ::std::option::Option::Some(__raw_value) = #crate_name::InputType::as_raw_value(#value) {
#crate_name::CustomValidator::check(&__custom_validator, __raw_value)
.map_err(|err_msg| #crate_name::InputValueError::<#ty>::custom(err_msg)) #map_err ?;
}
});
}
}
if codes.is_empty() {
return Ok(quote!());
}
let codes = codes.into_iter().map(|s| quote!(#s #map_err ?));
if self.list {
Ok(quote! {
for __item in #value {
if let ::std::option::Option::Some(__raw_value) = #crate_name::InputType::as_raw_value(__item) {
#(#codes;)*
}
}
})
} else {
Ok(quote! {
if let ::std::option::Option::Some(__raw_value) = #crate_name::InputType::as_raw_value(#value) {
#(#codes;)*
}
})
}
Ok(quote!(#(#codes)*))
}
}

View File

@ -605,3 +605,61 @@ pub async fn test_validate_wrapper_types() {
}]
);
}
#[tokio::test]
pub async fn test_list_both_max_items_and_max_length() {
struct Query;
#[Object]
impl Query {
async fn value(
&self,
#[graphql(validator(list, max_length = 3, max_items = 2))] values: Vec<String>,
) -> String {
values.into_iter().collect()
}
}
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
assert_eq!(
schema
.execute(r#"{ value(values: ["a", "b", "cdef"])}"#)
.await
.into_result()
.unwrap_err(),
vec![ServerError {
message: r#"Failed to parse "[String!]": the value length is 3, must be less than or equal to 2"#.to_string(),
source: None,
locations: vec![Pos { column: 17, line: 1}],
path: vec![PathSegment::Field("value".to_string())],
extensions: None
}]
);
assert_eq!(
schema
.execute(r#"{ value(values: ["a", "cdef"])}"#)
.await
.into_result()
.unwrap_err(),
vec![ServerError {
message: r#"Failed to parse "String": the string length is 4, must be less than or equal to 3"#.to_string(),
source: None,
locations: vec![Pos { column: 17, line: 1}],
path: vec![PathSegment::Field("value".to_string())],
extensions: None
}]
);
assert_eq!(
schema
.execute(r#"{ value(values: ["a", "b"])}"#)
.await
.into_result()
.unwrap()
.data,
value!({
"value": "ab"
})
);
}