Introduce process_with for input object

This commit is contained in:
Douman 2022-02-10 13:38:28 +09:00
parent aa15be435a
commit ca1f9045cc
4 changed files with 111 additions and 5 deletions

View File

@ -372,6 +372,8 @@ pub struct InputObjectField {
pub skip: bool,
#[darling(default)]
pub skip_input: bool,
#[darling(default)]
pub process_with: Option<String>,
// for SimpleObject
#[darling(default)]
pub skip_output: bool,

View File

@ -75,6 +75,16 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
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
.validator
.clone()
@ -99,9 +109,11 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
});
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)))
).map_err(#crate_name::InputValueError::propagate)?;
#process_with
#validators
});
@ -137,8 +149,12 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
let #ident: #ty = {
match obj.get(#name) {
::std::option::Option::Some(value) => {
#crate_name::InputType::parse(::std::option::Option::Some(::std::clone::Clone::clone(&value)))
.map_err(#crate_name::InputValueError::propagate)?
#[allow(unused_mut)]
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,
}
@ -147,9 +163,10 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
});
} else {
get_fields.push(quote! {
#[allow(non_snake_case)]
let #ident: #ty = #crate_name::InputType::parse(obj.get(#name).cloned())
#[allow(non_snake_case, unused_mut)]
let mut #ident: #ty = #crate_name::InputType::parse(obj.get(#name).cloned())
.map_err(#crate_name::InputValueError::propagate)?;
#process_with
#validators
});
}

View File

@ -24,6 +24,8 @@ Define a GraphQL input object
| 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_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 | 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 |

View File

@ -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",
}
})
);
}