Supported CharsMinLength and CharsMaxLength to count chars

This commit is contained in:
smihica 2021-10-14 00:42:10 +09:00
parent cf74a6c5d2
commit 5fd0561a3a
3 changed files with 274 additions and 3 deletions

View File

@ -8,7 +8,9 @@ use crate::{Error, Value};
pub use int_validators::{IntEqual, IntGreaterThan, IntLessThan, IntNonZero, IntRange};
pub use list_validators::{List, ListMaxLength, ListMinLength};
pub use string_validators::{Email, StringMaxLength, StringMinLength, MAC};
pub use string_validators::{
CharsMaxLength, CharsMinLength, Email, StringMaxLength, StringMinLength, MAC,
};
/// Input value validator
///

View File

@ -52,6 +52,54 @@ impl InputValueValidator for StringMaxLength {
}
}
/// Chars minimum length validator. This supports multibyte character.
pub struct CharsMinLength {
/// Must be greater than or equal to this value.
pub length: i32,
}
impl InputValueValidator for CharsMinLength {
fn is_valid(&self, value: &Value) -> Result<(), String> {
if let Value::String(s) = value {
if s.chars().count() < self.length as usize {
Err(format!(
"the value chars count is {}, must be greater than or equal to {}",
s.chars().count(),
self.length
))
} else {
Ok(())
}
} else {
Ok(())
}
}
}
/// Chars maximum length validator. This supports multibyte characters.
pub struct CharsMaxLength {
/// Must be less than or equal to this value.
pub length: i32,
}
impl InputValueValidator for CharsMaxLength {
fn is_valid(&self, value: &Value) -> Result<(), String> {
if let Value::String(s) = value {
if s.chars().count() > self.length as usize {
Err(format!(
"the value chars count is {}, must be less than or equal to {}",
s.chars().count(),
self.length
))
} else {
Ok(())
}
} else {
Ok(())
}
}
}
static EMAIL_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new("^(([0-9A-Za-z!#$%&'*+-/=?^_`{|}~&&[^@]]+)|(\"([0-9A-Za-z!#$%&'*+-/=?^_`{|}~ \"(),:;<>@\\[\\\\\\]]+)\"))@").unwrap()
});

View File

@ -1,6 +1,7 @@
use async_graphql::validators::{
Email, InputValueValidator, IntEqual, IntGreaterThan, IntLessThan, IntNonZero, IntRange,
ListMaxLength, ListMinLength, StringMaxLength, StringMinLength, MAC,
CharsMaxLength, CharsMinLength, Email, InputValueValidator, IntEqual, IntGreaterThan,
IntLessThan, IntNonZero, IntRange, ListMaxLength, ListMinLength, StringMaxLength,
StringMinLength, MAC,
};
use async_graphql::*;
@ -230,6 +231,226 @@ pub async fn test_input_validator_string_max_length() {
}
}
#[tokio::test]
pub async fn test_input_validator_chars_min_length() {
struct QueryRoot;
#[derive(InputObject)]
struct InputMaxLength {
#[graphql(validator(CharsMinLength(length = "6")))]
pub id: String,
}
#[Object]
impl QueryRoot {
async fn field_parameter(
&self,
#[graphql(validator(CharsMinLength(length = "6")))] _id: String,
) -> bool {
true
}
async fn input_object(&self, _input: InputMaxLength) -> bool {
true
}
}
let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
let test_cases = ["一2三4五", "壹2叁4伍6", "一2三4五6七", "壹2叁4伍6Ⅶ8"];
let validator_length = 6;
for case in &test_cases {
let field_query = format!("{{fieldParameter(id: \"{}\")}}", case);
let object_query = format!("{{inputObject(input: {{id: \"{}\"}})}}", case);
let case_length = case.chars().count();
if case_length < validator_length {
let should_fail_msg = format!(
"CharsMinLength case {} should have failed, but did not",
case
);
let field_error_msg = format!(
"Invalid value for argument \"id\", the value chars count is {}, must be greater than or equal to {}",
case_length, validator_length
);
let object_error_msg = format!(
"Invalid value for argument \"input.id\", the value chars count is {}, must be greater than or equal to {}",
case_length, validator_length
);
assert_eq!(
schema
.execute(&field_query)
.await
.into_result()
.expect_err(&should_fail_msg),
vec![ServerError {
message: field_error_msg,
locations: vec!(Pos {
line: 1,
column: 17
}),
path: Vec::new(),
extensions: None,
}]
);
assert_eq!(
schema
.execute(&object_query)
.await
.into_result()
.expect_err(&should_fail_msg[..]),
vec![ServerError {
message: object_error_msg,
locations: vec!(Pos {
line: 1,
column: 14
}),
path: Vec::new(),
extensions: None,
}]
);
} else {
let error_msg = format!("Schema returned error with test_string = {}", case);
assert_eq!(
schema
.execute(&field_query)
.await
.into_result()
.expect(&error_msg[..])
.data,
value!({"fieldParameter": true}),
"Failed to validate {} with CharsMinLength",
case
);
assert_eq!(
schema
.execute(&object_query)
.await
.into_result()
.expect(&error_msg[..])
.data,
value!({"inputObject": true}),
"Failed to validate {} with CharsMinLength",
case
);
}
}
}
#[tokio::test]
pub async fn test_input_validator_chars_max_length() {
struct QueryRoot;
#[derive(InputObject)]
struct InputMaxLength {
#[graphql(validator(CharsMaxLength(length = "6")))]
pub id: String,
}
#[Object]
impl QueryRoot {
async fn field_parameter(
&self,
#[graphql(validator(CharsMaxLength(length = "6")))] _id: String,
) -> bool {
true
}
async fn input_object(&self, _input: InputMaxLength) -> bool {
true
}
}
let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
let test_cases = ["一2三4五", "壹2叁4伍6", "一2三4五6七", "壹2叁4伍6Ⅶ8"];
let validator_length = 6;
for case in &test_cases {
let field_query = format!("{{fieldParameter(id: \"{}\")}}", case);
let object_query = format!("{{inputObject(input: {{id: \"{}\"}})}}", case);
let case_length = case.chars().count();
if case_length > validator_length {
let should_fail_msg = format!(
"CharsMaxLength case {} should have failed, but did not",
case
);
let field_error_msg = format!(
"Invalid value for argument \"id\", the value chars count is {}, must be less than or equal to {}",
case_length,
validator_length
);
let object_error_msg = format!(
"Invalid value for argument \"input.id\", the value chars count is {}, must be less than or equal to {}",
case_length,
validator_length
);
assert_eq!(
schema
.execute(&field_query)
.await
.into_result()
.expect_err(&should_fail_msg[..]),
vec![ServerError {
message: field_error_msg,
locations: vec!(Pos {
line: 1,
column: 17
}),
path: Vec::new(),
extensions: None,
}]
);
assert_eq!(
schema
.execute(&object_query)
.await
.into_result()
.expect_err(&should_fail_msg[..]),
vec![ServerError {
message: object_error_msg,
locations: vec!(Pos {
line: 1,
column: 14
}),
path: Vec::new(),
extensions: None,
}]
);
} else {
let error_msg = format!("Schema returned error with test_string = {}", case);
assert_eq!(
schema
.execute(&field_query)
.await
.into_result()
.expect(&error_msg[..])
.data,
value!({"fieldParameter": true}),
"Failed to validate {} with CharsMaxLength",
case
);
assert_eq!(
schema
.execute(&object_query)
.await
.into_result()
.expect(&error_msg[..])
.data,
value!({"inputObject": true}),
"Failed to validate {} with CharsMaxLength",
case
);
}
}
}
#[tokio::test]
pub async fn test_input_validator_string_email() {
struct QueryRoot;