Merge branch 'master' into lets-avoid-dupes
This commit is contained in:
commit
bd578d59f2
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -4,7 +4,25 @@ 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.11] 2021-12-02
|
||||
|
||||
- Fix panic on f32-64::INFINITE/f32-64::NEG_INFINITE/f32-64::NAN output. [#735](https://github.com/async-graphql/async-graphql/issues/735)
|
||||
|
||||
## [3.0.10] 2021-11-30
|
||||
|
||||
- Fix the custom validator cannot work on `Option<Vec<T>>`.
|
||||
|
||||
## [3.0.9] 2021-11-30
|
||||
|
||||
- Fix the validator cannot work on `Option<Vec<T>>`.
|
||||
|
||||
## [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`.
|
||||
- Add support for `SmolStr` via a feature. [#730](https://github.com/async-graphql/async-graphql/pull/730)
|
||||
|
||||
## [3.0.7] 2021-11-23
|
||||
|
||||
- Fix error extensions cause stack overflow. [#719](https://github.com/async-graphql/async-graphql/issues/719)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "async-graphql"
|
||||
version = "3.0.7"
|
||||
version = "3.0.11"
|
||||
authors = ["sunli <scott_s829@163.com>", "Koxiaet"]
|
||||
edition = "2021"
|
||||
description = "A GraphQL server library implemented in Rust"
|
||||
|
@ -61,6 +61,7 @@ opentelemetry = { version = "0.16.0", optional = true, default-features = false,
|
|||
uuid = { version = "0.8.2", optional = true, features = ["v4", "serde"] }
|
||||
rust_decimal = { version = "1.14.3", optional = true }
|
||||
url = { version = "2.2.1", optional = true }
|
||||
smol_str = { version = "0.1.21", optional = true }
|
||||
|
||||
# Non-feature optional dependencies
|
||||
blocking = { version = "1.0.2", optional = true }
|
||||
|
|
|
@ -78,6 +78,7 @@ This crate offers the following features, all of which are not activated by defa
|
|||
- `secrecy`: Integrate with the [`secrecy` crate](https://crates.io/crates/secrecy).
|
||||
- `decimal`: Integrate with the [`rust_decimal` crate](https://crates.io/crates/rust_decimal).
|
||||
- `cbor`: Support for [serde_cbor](https://crates.io/crates/serde_cbor).
|
||||
- `smol_str`: Integrate with the [`smol_str` crate](https://crates.io/crates/smol_str).
|
||||
|
||||
## Apollo Studio
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "async-graphql-derive"
|
||||
version = "3.0.8"
|
||||
version = "3.0.10"
|
||||
authors = ["sunli <scott_s829@163.com>", "Koxiaet"]
|
||||
edition = "2021"
|
||||
description = "Macros for async-graphql"
|
||||
|
|
|
@ -71,115 +71,147 @@ 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! {
|
||||
if let ::std::option::Option::Some(value) = #crate_name::InputType::as_raw_value(#value) {
|
||||
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;
|
||||
if let ::std::option::Option::Some(value) = #crate_name::InputType::as_raw_value(#value) {
|
||||
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)*))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,9 +58,8 @@ impl MyValidator {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl CustomValidator<i32> for MyValidator {
|
||||
async fn check(&self, value: &i32) -> Result<(), String> {
|
||||
fn check(&self, value: &i32) -> Result<(), String> {
|
||||
if *value == self.expect {
|
||||
Ok(())
|
||||
} else {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Poem
|
||||
# Actix-web
|
||||
|
||||
## Request example
|
||||
|
||||
|
|
|
@ -58,9 +58,8 @@ impl MyValidator {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl CustomValidator<i32> for MyValidator {
|
||||
async fn check(&self, value: &i32) -> Result<(), String> {
|
||||
fn check(&self, value: &i32) -> Result<(), String> {
|
||||
if *value == self.expect {
|
||||
Ok(())
|
||||
} else {
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
//! - `dataloader`: Support [DataLoader](dataloader/struct.DataLoader.html).
|
||||
//! - `decimal`: Integrate with the [`rust_decimal` crate](https://crates.io/crates/rust_decimal).
|
||||
//! - `cbor`: Support for [serde_cbor](https://crates.io/crates/serde_cbor).
|
||||
//! - `smol_str`: Integrate with the [`smol_str` crate](https://crates.io/crates/smol_str).
|
||||
//!
|
||||
//! ## Integrations
|
||||
//!
|
||||
|
|
10
src/types/external/floats.rs
vendored
10
src/types/external/floats.rs
vendored
|
@ -18,7 +18,10 @@ impl ScalarType for f32 {
|
|||
}
|
||||
|
||||
fn to_value(&self) -> Value {
|
||||
Value::Number(Number::from_f64(*self as f64).unwrap())
|
||||
match Number::from_f64(*self as f64) {
|
||||
Some(n) => Value::Number(n),
|
||||
None => Value::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,6 +43,9 @@ impl ScalarType for f64 {
|
|||
}
|
||||
|
||||
fn to_value(&self) -> Value {
|
||||
Value::Number(Number::from_f64(*self as f64).unwrap())
|
||||
match Number::from_f64(*self as f64) {
|
||||
Some(n) => Value::Number(n),
|
||||
None => Value::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2
src/types/external/mod.rs
vendored
2
src/types/external/mod.rs
vendored
|
@ -26,6 +26,8 @@ mod duration;
|
|||
mod naive_time;
|
||||
#[cfg(feature = "secrecy")]
|
||||
mod secrecy;
|
||||
#[cfg(feature = "smol_str")]
|
||||
mod smol_str;
|
||||
#[cfg(feature = "url")]
|
||||
mod url;
|
||||
#[cfg(feature = "uuid")]
|
||||
|
|
25
src/types/external/smol_str.rs
vendored
Normal file
25
src/types/external/smol_str.rs
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
use smol_str::SmolStr;
|
||||
|
||||
use crate::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
|
||||
|
||||
#[Scalar(
|
||||
internal,
|
||||
name = "SmolStr",
|
||||
specified_by_url = "https://docs.rs/smol_str/latest/smol_str/struct.SmolStr.html"
|
||||
)]
|
||||
impl ScalarType for SmolStr {
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
Value::String(s) => Ok(SmolStr::new(s)),
|
||||
_ => Err(InputValueError::expected_type(value)),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid(value: &Value) -> bool {
|
||||
matches!(value, Value::String(_))
|
||||
}
|
||||
|
||||
fn to_value(&self) -> Value {
|
||||
Value::String(self.to_string())
|
||||
}
|
||||
}
|
|
@ -93,6 +93,61 @@ impl<T: Serialize + Send + Sync> OutputType for Json<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl InputType for serde_json::Value {
|
||||
type RawValueType = serde_json::Value;
|
||||
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("JSON")
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut Registry) -> String {
|
||||
registry.create_output_type::<serde_json::Value, _>(|_| MetaType::Scalar {
|
||||
name: <Self as InputType>::type_name().to_string(),
|
||||
description: None,
|
||||
is_valid: |_| true,
|
||||
visible: None,
|
||||
specified_by_url: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse(value: Option<Value>) -> InputValueResult<Self> {
|
||||
Ok(from_value(value.unwrap_or_default())?)
|
||||
}
|
||||
|
||||
fn to_value(&self) -> Value {
|
||||
to_value(&self).unwrap_or_default()
|
||||
}
|
||||
|
||||
fn as_raw_value(&self) -> Option<&Self::RawValueType> {
|
||||
Some(&self)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl OutputType for serde_json::Value {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("JSON")
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut Registry) -> String {
|
||||
registry.create_output_type::<serde_json::Value, _>(|_| MetaType::Scalar {
|
||||
name: <Self as OutputType>::type_name().to_string(),
|
||||
description: None,
|
||||
is_valid: |_| true,
|
||||
visible: None,
|
||||
specified_by_url: None,
|
||||
})
|
||||
}
|
||||
|
||||
async fn resolve(
|
||||
&self,
|
||||
_ctx: &ContextSelectionSet<'_>,
|
||||
_field: &Positioned<Field>,
|
||||
) -> ServerResult<Value> {
|
||||
Ok(to_value(&self).ok().unwrap_or_default())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::*;
|
||||
|
|
|
@ -45,3 +45,26 @@ pub async fn test_scalar_macro() {
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
pub async fn test_float_inf() {
|
||||
struct Query;
|
||||
|
||||
#[Object]
|
||||
impl Query {
|
||||
async fn value(&self) -> f32 {
|
||||
f32::INFINITY
|
||||
}
|
||||
}
|
||||
|
||||
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute("{ value }")
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap()
|
||||
.data,
|
||||
value!({ "value": null })
|
||||
);
|
||||
}
|
||||
|
|
|
@ -341,6 +341,20 @@ pub async fn test_custom_validator() {
|
|||
async fn input(&self, input: MyInput) -> i32 {
|
||||
input.n
|
||||
}
|
||||
|
||||
async fn value2(
|
||||
&self,
|
||||
#[graphql(validator(list, custom = "MyValidator::new(100)"))] values: Vec<i32>,
|
||||
) -> i32 {
|
||||
values.into_iter().sum()
|
||||
}
|
||||
|
||||
async fn value3(
|
||||
&self,
|
||||
#[graphql(validator(list, custom = "MyValidator::new(100)"))] values: Option<Vec<i32>>,
|
||||
) -> i32 {
|
||||
values.into_iter().flatten().sum()
|
||||
}
|
||||
}
|
||||
|
||||
struct Subscription;
|
||||
|
@ -444,6 +458,54 @@ pub async fn test_custom_validator() {
|
|||
extensions: None
|
||||
}]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute("{ value2(values: [77, 88] ) }")
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap_err(),
|
||||
vec![ServerError {
|
||||
message: r#"Failed to parse "[Int!]": expect 100, actual 77"#.to_string(),
|
||||
source: None,
|
||||
locations: vec![Pos {
|
||||
line: 1,
|
||||
column: 18
|
||||
}],
|
||||
path: vec![PathSegment::Field("value2".to_string())],
|
||||
extensions: None
|
||||
}]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute("{ value3(values: [77, 88] ) }")
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap_err(),
|
||||
vec![ServerError {
|
||||
message: r#"Failed to parse "[Int!]": expect 100, actual 77"#.to_string(),
|
||||
source: None,
|
||||
locations: vec![Pos {
|
||||
line: 1,
|
||||
column: 18
|
||||
}],
|
||||
path: vec![PathSegment::Field("value3".to_string())],
|
||||
extensions: None
|
||||
}]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute("{ value3(values: null ) }")
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap()
|
||||
.data,
|
||||
value!({
|
||||
"value3": 0
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
@ -605,3 +667,125 @@ 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()
|
||||
}
|
||||
|
||||
async fn value2(
|
||||
&self,
|
||||
#[graphql(validator(list, max_length = 3, max_items = 2))] values: Option<Vec<String>>,
|
||||
) -> String {
|
||||
values.into_iter().flatten().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"
|
||||
})
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute(r#"{ value2(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: 18, line: 1}],
|
||||
path: vec![PathSegment::Field("value2".to_string())],
|
||||
extensions: None
|
||||
}]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute(r#"{ value2(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: 18, line: 1}],
|
||||
path: vec![PathSegment::Field("value2".to_string())],
|
||||
extensions: None
|
||||
}]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute(r#"{ value2(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: 18, line: 1}],
|
||||
path: vec![PathSegment::Field("value2".to_string())],
|
||||
extensions: None
|
||||
}]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute(r#"{ value2(values: null)}"#)
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap()
|
||||
.data,
|
||||
value!({
|
||||
"value2": ""
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user