Skip to content

Commit 5d7f99e

Browse files
authored
Merge pull request #131 from Shopify/ap.input-object-deserialize
Add `Deserialize` implementations for input objects
2 parents ecb6270 + e5edf12 commit 5d7f99e

File tree

5 files changed

+106
-3
lines changed

5 files changed

+106
-3
lines changed

example_with_targets/schema.graphql

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ Only allow the field to be queried when targeting one of the specified targets.
88
"""
99
directive @restrictTarget(only: [String!]!) on FIELD_DEFINITION
1010

11+
"""
12+
Requires that exactly one field must be supplied and that field must not be `null`.
13+
"""
14+
directive @oneOf on INPUT_OBJECT
15+
1116
"""
1217
Represents an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)-encoded date string.
1318
For example, September 7, 2019 is represented as `"2019-07-16"`.
@@ -108,6 +113,20 @@ The result of API target B.
108113
"""
109114
input FunctionTargetBResult {
110115
name: String
116+
operations: [Operation!]!
117+
}
118+
119+
input Operation @oneOf {
120+
doThis: This
121+
doThat: That
122+
}
123+
124+
input This {
125+
thisField: String!
126+
}
127+
128+
input That {
129+
thatField: Int!
111130
}
112131

113132
"""

example_with_targets/src/main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ fn target_a(_input: schema::target_a::Input) -> Result<schema::FunctionTargetARe
2323
fn target_b(input: schema::target_b::Input) -> Result<schema::FunctionTargetBResult> {
2424
Ok(schema::FunctionTargetBResult {
2525
name: Some(format!("new name: \"{}\"", input.id())),
26+
operations: vec![
27+
schema::Operation::DoThis(schema::This {
28+
this_field: "this field".to_string(),
29+
}),
30+
schema::Operation::DoThat(schema::That { that_field: 42 }),
31+
],
2632
})
2733
}
2834

example_with_targets/src/tests.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ fn test_target_b() -> Result<()> {
3131
)?;
3232
let expected = crate::schema::FunctionTargetBResult {
3333
name: Some("new name: \"gid://shopify/Order/1234567890\"".to_string()),
34+
operations: vec![
35+
crate::schema::Operation::DoThis(crate::schema::This {
36+
this_field: "this field".to_string(),
37+
}),
38+
crate::schema::Operation::DoThat(crate::schema::That { that_field: 42 }),
39+
],
3440
};
3541

3642
assert_eq!(result, expected);

integration_tests/tests/integration_test.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,19 @@ fn test_example_with_targets_target_b() -> Result<()> {
3939
assert_eq!(
4040
output,
4141
serde_json::json!({
42-
"name": "new name: \"gid://shopify/Order/1234567890\""
42+
"name": "new name: \"gid://shopify/Order/1234567890\"",
43+
"operations": [
44+
{
45+
"doThis": {
46+
"thisField": "this field"
47+
}
48+
},
49+
{
50+
"doThat": {
51+
"thatField": 42
52+
}
53+
}
54+
]
4355
})
4456
);
4557
Ok(())

shopify_function_macro/src/lib.rs

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,27 @@ impl CodeGenerator for ShopifyFunctionCodeGenerator {
529529
}
530530
};
531531

532-
vec![serialize_impl]
532+
let field_values: Vec<syn::FieldValue> = input_object_type_definition
533+
.input_field_definitions()
534+
.iter()
535+
.map(|ivd| {
536+
let field_name_ident = names::field_ident(ivd.name());
537+
let field_name_lit_str = syn::LitStr::new(ivd.name(), Span::mixed_site());
538+
parse_quote! { #field_name_ident: shopify_function::wasm_api::Deserialize::deserialize(&value.get_obj_prop(#field_name_lit_str))? }
539+
})
540+
.collect();
541+
542+
let deserialize_impl = parse_quote! {
543+
impl shopify_function::wasm_api::Deserialize for #name_ident {
544+
fn deserialize(value: &shopify_function::wasm_api::Value) -> ::std::result::Result<Self, shopify_function::wasm_api::read::Error> {
545+
Ok(Self {
546+
#(#field_values),*
547+
})
548+
}
549+
}
550+
};
551+
552+
vec![serialize_impl, deserialize_impl]
533553
}
534554

535555
fn additional_impls_for_one_of_input_object(
@@ -567,7 +587,47 @@ impl CodeGenerator for ShopifyFunctionCodeGenerator {
567587
}
568588
};
569589

570-
vec![serialize_impl]
590+
let deserialize_match_arms: Vec<syn::Arm> = input_object_type_definition
591+
.input_field_definitions()
592+
.iter()
593+
.map(|ivd| {
594+
let field_name_lit_str = syn::LitStr::new(ivd.name(), Span::mixed_site());
595+
let variant_ident = names::enum_variant_ident(ivd.name());
596+
597+
parse_quote! {
598+
#field_name_lit_str => {
599+
let value = shopify_function::wasm_api::Deserialize::deserialize(&field_value)?;
600+
Ok(Self::#variant_ident(value))
601+
}
602+
}
603+
})
604+
.collect();
605+
606+
let deserialize_impl = parse_quote! {
607+
impl shopify_function::wasm_api::Deserialize for #name_ident {
608+
fn deserialize(value: &shopify_function::wasm_api::Value) -> ::std::result::Result<Self, shopify_function::wasm_api::read::Error> {
609+
let Some(obj_len) = value.obj_len() else {
610+
return Err(shopify_function::wasm_api::read::Error::InvalidType);
611+
};
612+
613+
if obj_len != 1 {
614+
return Err(shopify_function::wasm_api::read::Error::InvalidType);
615+
}
616+
617+
let Some(field_name) = value.get_obj_key_at_index(0) else {
618+
return Err(shopify_function::wasm_api::read::Error::InvalidType);
619+
};
620+
let field_value = value.get_at_index(0);
621+
622+
match field_name.as_str() {
623+
#(#deserialize_match_arms)*
624+
_ => Err(shopify_function::wasm_api::read::Error::InvalidType),
625+
}
626+
}
627+
}
628+
};
629+
630+
vec![serialize_impl, deserialize_impl]
571631
}
572632

573633
fn attributes_for_enum(

0 commit comments

Comments
 (0)