Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions example_with_targets/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ input FunctionTargetAResult {
status: Int
}

input SerializationProbe {
optionalValue: Int
defaultedValue: Int! = 200
requiredValue: Int!
}

"""
The result of API target B.
"""
Expand Down
47 changes: 47 additions & 0 deletions example_with_targets/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::*;
use shopify_function::wasm_api::{Context, Serialize};
use shopify_function::{run_function_with_input, Result};

#[test]
Expand Down Expand Up @@ -42,3 +43,49 @@ fn test_target_b() -> Result<()> {
assert_eq!(result, expected);
Ok(())
}

#[test]
fn test_input_object_serialization_omits_none_fields_and_keeps_required_fields() -> Result<()> {
let result = serialize_to_json(&crate::schema::SerializationProbe {
optional_value: None,
defaulted_value: None,
required_value: 1,
})?;

assert_eq!(serde_json::json!({ "requiredValue": 1 }), result);
Ok(())
}

#[test]
fn test_input_object_serialization_includes_some_fields() -> Result<()> {
let result = serialize_to_json(&crate::schema::SerializationProbe {
optional_value: Some(200),
defaulted_value: Some(201),
required_value: 1,
})?;

assert_eq!(
serde_json::json!({ "optionalValue": 200, "defaultedValue": 201, "requiredValue": 1 }),
result
);
Ok(())
}

#[test]
fn test_one_of_input_object_serialization_writes_active_variant() -> Result<()> {
let result = serialize_to_json(&crate::schema::Operation::DoThis(crate::schema::This {
this_field: "this field".to_string(),
}))?;

assert_eq!(
serde_json::json!({ "doThis": { "thisField": "this field" } }),
result
);
Ok(())
}

fn serialize_to_json<T: Serialize + ?Sized>(value: &T) -> Result<serde_json::Value> {
let mut context = Context::new_with_input(serde_json::json!({}));
value.serialize(&mut context)?;
Ok(context.finalize_output_and_return()?)
}
45 changes: 35 additions & 10 deletions shopify_function_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,28 +500,53 @@ impl CodeGenerator for ShopifyFunctionCodeGenerator {
let field_name_ident = names::field_ident(ivd.name());
let field_name_lit_str = syn::LitStr::new(ivd.name(), Span::mixed_site());

vec![
parse_quote! {
context.write_utf8_str(#field_name_lit_str)?;
},
parse_quote! {
self.#field_name_ident.serialize(context)?;
},
]
if ivd.is_required() {
vec![
parse_quote! {
context.write_utf8_str(#field_name_lit_str)?;
},
parse_quote! {
self.#field_name_ident.serialize(context)?;
},
]
} else {
vec![parse_quote! {
if let ::std::option::Option::Some(value) = &self.#field_name_ident {
context.write_utf8_str(#field_name_lit_str)?;
value.serialize(context)?;
}
}]
}
})
.collect();

let num_fields = input_object_type_definition.input_field_definitions().len();
let num_required_fields = input_object_type_definition
.input_field_definitions()
.iter()
.filter(|ivd| ivd.is_required())
.count();

let optional_field_count_terms: Vec<syn::Expr> = input_object_type_definition
.input_field_definitions()
.iter()
.filter(|ivd| !ivd.is_required())
.map(|ivd| {
let field_name_ident = names::field_ident(ivd.name());
parse_quote! { ::std::primitive::usize::from(self.#field_name_ident.is_some()) }
})
.collect();

let serialize_impl = parse_quote! {
impl shopify_function::wasm_api::Serialize for #name_ident {
fn serialize(&self, context: &mut shopify_function::wasm_api::Context) -> ::std::result::Result<(), shopify_function::wasm_api::write::Error> {
let field_count: ::std::primitive::usize = #num_required_fields #(+ #optional_field_count_terms)*;

context.write_object(
|context| {
#(#field_statements)*
::std::result::Result::Ok(())
},
#num_fields,
field_count,
)
}
}
Expand Down
Loading