Introduce process_with for input object
This commit is contained in:
parent
9318c0731d
commit
6181b6bcd7
|
@ -372,6 +372,8 @@ pub struct InputObjectField {
|
||||||
pub skip: bool,
|
pub skip: bool,
|
||||||
#[darling(default)]
|
#[darling(default)]
|
||||||
pub skip_input: bool,
|
pub skip_input: bool,
|
||||||
|
#[darling(default)]
|
||||||
|
pub process_with: Option<String>,
|
||||||
// for SimpleObject
|
// for SimpleObject
|
||||||
#[darling(default)]
|
#[darling(default)]
|
||||||
pub skip_output: bool,
|
pub skip_output: bool,
|
||||||
|
|
|
@ -75,6 +75,16 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
|
||||||
|
|
||||||
federation_fields.push((ty, name.clone()));
|
federation_fields.push((ty, name.clone()));
|
||||||
|
|
||||||
|
let process_with = match field.process_with.as_ref() {
|
||||||
|
Some(fn_path) => {
|
||||||
|
let fn_path: syn::ExprPath = syn::parse_str(&fn_path)?;
|
||||||
|
quote! {
|
||||||
|
#fn_path(&mut #ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
let validators = field
|
let validators = field
|
||||||
.validator
|
.validator
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -99,9 +109,11 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
|
||||||
});
|
});
|
||||||
|
|
||||||
get_fields.push(quote! {
|
get_fields.push(quote! {
|
||||||
let #ident: #ty = #crate_name::InputType::parse(
|
#[allow(unused_mut)]
|
||||||
|
let mut #ident: #ty = #crate_name::InputType::parse(
|
||||||
::std::option::Option::Some(#crate_name::Value::Object(::std::clone::Clone::clone(&obj)))
|
::std::option::Option::Some(#crate_name::Value::Object(::std::clone::Clone::clone(&obj)))
|
||||||
).map_err(#crate_name::InputValueError::propagate)?;
|
).map_err(#crate_name::InputValueError::propagate)?;
|
||||||
|
#process_with
|
||||||
#validators
|
#validators
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -137,8 +149,12 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
|
||||||
let #ident: #ty = {
|
let #ident: #ty = {
|
||||||
match obj.get(#name) {
|
match obj.get(#name) {
|
||||||
::std::option::Option::Some(value) => {
|
::std::option::Option::Some(value) => {
|
||||||
#crate_name::InputType::parse(::std::option::Option::Some(::std::clone::Clone::clone(&value)))
|
#[allow(unused_mut)]
|
||||||
.map_err(#crate_name::InputValueError::propagate)?
|
let mut #ident = #crate_name::InputType::parse(::std::option::Option::Some(::std::clone::Clone::clone(&value)))
|
||||||
|
.map_err(#crate_name::InputValueError::propagate)?;
|
||||||
|
#process_with
|
||||||
|
#ident
|
||||||
|
|
||||||
},
|
},
|
||||||
::std::option::Option::None => #default,
|
::std::option::Option::None => #default,
|
||||||
}
|
}
|
||||||
|
@ -147,9 +163,10 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
get_fields.push(quote! {
|
get_fields.push(quote! {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case, unused_mut)]
|
||||||
let #ident: #ty = #crate_name::InputType::parse(obj.get(#name).cloned())
|
let mut #ident: #ty = #crate_name::InputType::parse(obj.get(#name).cloned())
|
||||||
.map_err(#crate_name::InputValueError::propagate)?;
|
.map_err(#crate_name::InputValueError::propagate)?;
|
||||||
|
#process_with
|
||||||
#validators
|
#validators
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,8 @@ Define a GraphQL input object
|
||||||
| flatten | Similar to serde (flatten) | boolean | Y |
|
| flatten | Similar to serde (flatten) | boolean | Y |
|
||||||
| skip | Skip this field, use `Default::default` to get a default value for this field. | bool | Y |
|
| skip | Skip this field, use `Default::default` to get a default value for this field. | bool | Y |
|
||||||
| skip_input | Skip this field, similar to `skip`, but avoids conflicts when this macro is used with `SimpleObject`. | bool | Y |
|
| skip_input | Skip this field, similar to `skip`, but avoids conflicts when this macro is used with `SimpleObject`. | bool | Y |
|
||||||
|
| process_with | Upon successful parsing, invokes specified function. Its signature must be `fn(&mut T)`.
|
||||||
|
| code path | Y |
|
||||||
| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y |
|
| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y |
|
||||||
| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y |
|
| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y |
|
||||||
| secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y |
|
| secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y |
|
||||||
|
|
|
@ -546,3 +546,88 @@ pub async fn test_complex_output() {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
pub async fn test_input_object_process_with() {
|
||||||
|
mod processor {
|
||||||
|
pub fn string(input: &mut String) {
|
||||||
|
while let Some(ch) = input.pop() {
|
||||||
|
if !ch.is_whitespace() {
|
||||||
|
input.push(ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(InputObject)]
|
||||||
|
struct MyInput {
|
||||||
|
//processor does nothing on default value
|
||||||
|
#[graphql(default = " ", process_with = "processor::string")]
|
||||||
|
a: String,
|
||||||
|
|
||||||
|
#[graphql(process_with = "processor::string")]
|
||||||
|
b: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyOutput {
|
||||||
|
a: String,
|
||||||
|
b: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl MyOutput {
|
||||||
|
async fn a(&self) -> &String {
|
||||||
|
&self.a
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn b(&self) -> &String {
|
||||||
|
&self.b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Root;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Root {
|
||||||
|
async fn a(&self, input: MyInput) -> MyOutput {
|
||||||
|
MyOutput {
|
||||||
|
a: input.a,
|
||||||
|
b: input.b,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let schema = Schema::new(Root, EmptyMutation, EmptySubscription);
|
||||||
|
let query = r#"{
|
||||||
|
a(input:{b: "test b "}) {
|
||||||
|
a b
|
||||||
|
}
|
||||||
|
}"#
|
||||||
|
.to_owned();
|
||||||
|
assert_eq!(
|
||||||
|
schema.execute(&query).await.data,
|
||||||
|
value!({
|
||||||
|
"a": {
|
||||||
|
"a": " ",
|
||||||
|
"b": "test b",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let schema = Schema::new(Root, EmptyMutation, EmptySubscription);
|
||||||
|
let query = r#"{
|
||||||
|
a(input:{a: "test a ", b: "test"}) {
|
||||||
|
a b
|
||||||
|
}
|
||||||
|
}"#
|
||||||
|
.to_owned();
|
||||||
|
assert_eq!(
|
||||||
|
schema.execute(&query).await.data,
|
||||||
|
value!({
|
||||||
|
"a": {
|
||||||
|
"a": "test a",
|
||||||
|
"b": "test",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user