Add flatten type field support for input objects. #255

This commit is contained in:
Sunli 2020-09-03 20:00:33 +08:00
parent fbccfecbc0
commit 366c7c03da
3 changed files with 166 additions and 3 deletions

View File

@ -457,6 +457,7 @@ pub struct InputField {
pub desc: Option<String>,
pub default: Option<TokenStream>,
pub validator: TokenStream,
pub flatten: bool,
}
impl InputField {
@ -465,6 +466,7 @@ impl InputField {
let mut desc = None;
let mut default = None;
let mut validator = quote! { None };
let mut flatten = false;
for attr in attrs {
if attr.path.is_ident("field") {
@ -480,6 +482,9 @@ impl InputField {
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("default") => {
default = Some(quote! { Default::default() });
}
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("flatten") => {
flatten = true;
}
NestedMeta::Meta(Meta::NameValue(nv)) => {
if nv.path.is_ident("name") {
if let syn::Lit::Str(lit) = &nv.lit {
@ -523,6 +528,7 @@ impl InputField {
desc,
default,
validator,
flatten,
})
}
}

View File

@ -46,15 +46,42 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
let mut put_fields = Vec::new();
let mut fields = Vec::new();
let mut schema_fields = Vec::new();
let mut flatten_fields = Vec::new();
for field in &s.fields {
let field_args = args::InputField::parse(&crate_name, &field.attrs)?;
let ident = field.ident.as_ref().unwrap();
let ty = &field.ty;
let validator = &field_args.validator;
let name = field_args
.name
.unwrap_or_else(|| ident.unraw().to_string().to_camel_case());
if field_args.flatten {
flatten_fields.push((ident, ty));
schema_fields.push(quote! {
#ty::create_type_info(registry);
if let Some(#crate_name::registry::MetaType::InputObject{ input_fields, .. }) =
registry.types.remove(&*<#ty as #crate_name::Type>::type_name()) {
fields.extend(input_fields);
}
});
get_fields.push(quote! {
let #ident: #ty = #crate_name::InputValueType::parse(Some(#crate_name::Value::Object(obj.clone())))?;
});
fields.push(ident);
put_fields.push(quote! {
if let #crate_name::Value::Object(values) = #crate_name::InputValueType::to_value(&self.#ident) {
map.extend(values);
}
});
continue;
}
let validator = &field_args.validator;
let desc = field_args
.desc
.as_ref()
@ -139,5 +166,8 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
impl #crate_name::InputObjectType for #ident {}
};
if gql_typename == "MyInputObject11" {
println!("{}", expanded);
}
Ok(expanded.into())
}

View File

@ -91,10 +91,9 @@ pub async fn test_input_object_default_value() {
pub async fn test_inputobject_derive_and_item_attributes() {
use serde::Deserialize;
#[async_graphql::InputObject]
#[InputObject]
#[derive(Deserialize, PartialEq, Debug)]
struct MyInputObject {
#[field]
#[serde(alias = "other")]
real: i32,
}
@ -104,3 +103,131 @@ pub async fn test_inputobject_derive_and_item_attributes() {
MyInputObject { real: 100 }
);
}
#[async_std::test]
pub async fn test_inputobject_flatten() {
#[derive(GQLInputObject, Debug, Eq, PartialEq)]
struct A {
a: i32,
}
#[derive(GQLInputObject, Debug, Eq, PartialEq)]
struct B {
#[field(default = 70)]
b: i32,
#[field(flatten)]
a_obj: A,
}
#[derive(GQLInputObject, Debug, Eq, PartialEq)]
struct MyInputObject {
#[field(flatten)]
b_obj: B,
c: i32,
}
assert_eq!(
MyInputObject::parse(Some(
serde_json::json!({
"a": 10,
"b": 20,
"c": 30,
})
.into()
))
.unwrap(),
MyInputObject {
b_obj: B {
b: 20,
a_obj: A { a: 10 }
},
c: 30,
}
);
assert_eq!(
MyInputObject {
b_obj: B {
b: 20,
a_obj: A { a: 10 }
},
c: 30,
}
.to_value(),
serde_json::json!({
"a": 10,
"b": 20,
"c": 30,
})
.into()
);
struct Query;
#[Object]
impl Query {
async fn test(&self, input: MyInputObject) -> i32 {
input.c + input.b_obj.b + input.b_obj.a_obj.a
}
async fn test_with_default(
&self,
#[arg(default_with = r#"MyInputObject {
b_obj: B {
b: 2,
a_obj: A { a: 1 }
},
c: 3,
}"#)]
input: MyInputObject,
) -> i32 {
input.c + input.b_obj.b + input.b_obj.a_obj.a
}
}
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
assert_eq!(
schema
.execute(
r#"{
test(input:{a:10, b: 20, c: 30})
}"#
)
.await
.unwrap()
.data,
serde_json::json!({
"test": 60,
})
);
assert_eq!(
schema
.execute(
r#"{
test(input:{a:10, c: 30})
}"#
)
.await
.unwrap()
.data,
serde_json::json!({
"test": 110,
})
);
assert_eq!(
schema
.execute(
r#"{
testWithDefault
}"#
)
.await
.unwrap()
.data,
serde_json::json!({
"testWithDefault": 6,
})
);
}