Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions example_with_targets/cart.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
query Input {
cartLines {
id
quantity
title
}
}
27 changes: 27 additions & 0 deletions example_with_targets/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ A void type that can be used to return a null value from a mutation.
"""
scalar Void

"""
A cart line representing an item in the cart.
"""
type CartLine {
id: ID!
quantity: Int!
title: String!
}

"""
The input object for the function.
"""
Expand All @@ -77,6 +86,7 @@ type Input {
optionalArray: [String!]
optionalArrayOfArrays: [[String!]!]
optionalArrayOfOptionalArrays: [[String!]]
cartLines: [CartLine!] @restrictTarget(only: ["test.target-cart"])
}

"""
Expand All @@ -102,6 +112,16 @@ type MutationRoot {
"""
result: FunctionTargetBResult!
): Void!

"""
The function for API target Cart.
"""
targetCart(
"""
The result of calling the function for API target Cart.
"""
result: FunctionTargetCartResult!
): Void!
}

"""
Expand All @@ -119,6 +139,13 @@ input FunctionTargetBResult {
operations: [Operation!]!
}

"""
The result of API target Cart.
"""
input FunctionTargetCartResult {
totalQuantity: Int!
}

input Operation @oneOf {
doThis: This
doThat: That
Expand Down
17 changes: 17 additions & 0 deletions example_with_targets/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ mod schema {

#[query("./b.graphql")]
pub mod target_b {}

#[query("./cart.graphql")]
pub mod target_cart {}
}

#[shopify_function]
Expand Down Expand Up @@ -45,6 +48,20 @@ fn target_panic(_input: schema::target_a::Input) -> Result<schema::FunctionTarge
panic!("Something went wrong");
}

#[shopify_function]
fn target_cart(input: schema::target_cart::Input) -> Result<schema::FunctionTargetCartResult> {
// Iterate over cart lines and sum quantities - this accesses the `quantity` property
// multiple times, which should benefit from interned string caching
Comment thread
adampetro marked this conversation as resolved.
Outdated
let total_quantity: i32 = input
.cart_lines()
.unwrap_or(&[])
.iter()
.map(|line| *line.quantity())
.sum();

Ok(schema::FunctionTargetCartResult { total_quantity })
}

fn main() {
log!("Invoke a named export");
process::abort()
Expand Down
37 changes: 31 additions & 6 deletions integration_tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ use std::{
sync::LazyLock,
};

/// Result from running a Shopify Function
#[derive(Debug)]
pub struct RunResult {
/// The JSON output from the function
pub output: serde_json::Value,
/// The logs from the function
pub logs: String,
/// The number of instructions executed
pub instructions: u64,
/// The memory usage in bytes
pub memory_usage: u64,
}

const FUNCTION_RUNNER_VERSION: &str = "9.1.0";
const TRAMPOLINE_VERSION: &str = "2.0.0";

Expand Down Expand Up @@ -148,11 +161,7 @@ pub fn prepare_example(name: &str) -> Result<PathBuf> {
Ok(trampolined_path)
}

pub fn run_example(
path: PathBuf,
export: &str,
input: serde_json::Value,
) -> Result<(serde_json::Value, String)> {
pub fn run_example(path: PathBuf, export: &str, input: serde_json::Value) -> Result<RunResult> {
let function_runner_path = FUNCTION_RUNNER_PATH
.as_ref()
.map_err(|e| anyhow::anyhow!("Failed to download function runner: {}", e))?;
Expand Down Expand Up @@ -198,6 +207,16 @@ pub fn run_example(
.ok_or_else(|| anyhow::anyhow!("Logs are not a string"))?
.to_string();

let instructions = output
.get("instructions")
.and_then(|v| v.as_u64())
.ok_or_else(|| anyhow::anyhow!("No instructions count"))?;

let memory_usage = output
.get("memory_usage")
.and_then(|v| v.as_u64())
.ok_or_else(|| anyhow::anyhow!("No memory_usage"))?;

if !status.success() {
anyhow::bail!(
"Function runner returned non-zero exit code: {}, logs: {}",
Expand All @@ -210,5 +229,11 @@ pub fn run_example(
.get_mut("output")
.ok_or_else(|| anyhow::anyhow!("No output"))?
.take();
Ok((output_json, logs))

Ok(RunResult {
output: output_json,
logs,
instructions,
memory_usage,
})
}
55 changes: 48 additions & 7 deletions integration_tests/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@ fn test_example_with_targets_target_a() -> Result<()> {
"name": "test",
"country": "CA"
});
let (output, logs) = run_example(path.clone(), "target_a", input)?;
let result = run_example(path.clone(), "target_a", input)?;
assert_eq!(
output,
result.output,
serde_json::json!({
"status": 200
})
);
assert_eq!(logs, "In target_a\nWith var: 42\nWith var: 42\n");
assert_eq!(result.logs, "In target_a\nWith var: 42\nWith var: 42\n");
eprintln!(
"target_a: instructions={}, memory_usage={}",
result.instructions, result.memory_usage
);
Ok(())
}

Expand All @@ -36,9 +40,9 @@ fn test_example_with_targets_target_b() -> Result<()> {
"id": "gid://shopify/Order/1234567890",
"targetAResult": 200
});
let (output, logs) = run_example(path.clone(), "target_b", input)?;
let result = run_example(path.clone(), "target_b", input)?;
assert_eq!(
output,
result.output,
serde_json::json!({
"name": "new name: \"gid://shopify/Order/1234567890\"",
"operations": [
Expand All @@ -55,7 +59,11 @@ fn test_example_with_targets_target_b() -> Result<()> {
]
})
);
assert_eq!(logs, "In target_b\n");
assert_eq!(result.logs, "In target_b\n");
eprintln!(
"target_b: instructions={}, memory_usage={}",
result.instructions, result.memory_usage
);
Ok(())
}

Expand All @@ -72,10 +80,43 @@ fn test_example_with_panic() -> Result<()> {
.unwrap_err()
.to_string();
let expected_err =
"Function runner returned non-zero exit code: exit status: 1, logs: panicked at example_with_targets/src/main.rs:45:5:\nSomething went wrong\nerror while executing at wasm backtrace:";
"Function runner returned non-zero exit code: exit status: 1, logs: panicked at example_with_targets/src/main.rs:48:5:\nSomething went wrong\nerror while executing at wasm backtrace:";
assert!(
err.contains(expected_err),
"Expected error message to contain:\n`{expected_err}`\nbut was:\n`{err}`"
);
Ok(())
}

#[test]
fn test_example_with_targets_target_cart() -> Result<()> {
let path = EXAMPLE_WITH_TARGETS_RESULT
.as_ref()
.map_err(|e| anyhow::anyhow!("Failed to prepare example: {}", e))?;
// Create input with 100 cart lines to demonstrate interned string benefits
let cart_lines: Vec<_> = (0..100)
.map(|i| {
serde_json::json!({
"id": format!("gid://shopify/CartLine/{i}"),
"quantity": i + 1,
"title": format!("Product {i}")
})
})
.collect();
let input = serde_json::json!({
"cartLines": cart_lines
});
let result = run_example(path.clone(), "target_cart", input)?;
// Sum of 1 + 2 + ... + 100 = 5050
assert_eq!(
result.output,
serde_json::json!({
"totalQuantity": 5050
})
);
eprintln!(
"target_cart (100 lines): instructions={}, memory_usage={}",
result.instructions, result.memory_usage
);
Ok(())
}
5 changes: 1 addition & 4 deletions shopify_function_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,11 +349,8 @@ impl CodeGenerator for ShopifyFunctionCodeGenerator {
parse_quote! {
#description
pub fn #field_name_ident(&self) -> #field_type {
static INTERNED_FIELD_NAME: shopify_function::wasm_api::CachedInternedStringId = shopify_function::wasm_api::CachedInternedStringId::new(#field_name_lit_str, );
let interned_string_id = INTERNED_FIELD_NAME.load();

let value = self.#field_name_ident.get_or_init(|| {
let value = self.__wasm_value.get_interned_obj_prop(interned_string_id);
let value = self.__wasm_value.get_obj_prop(#field_name_lit_str);
shopify_function::wasm_api::Deserialize::deserialize(&value).unwrap()
});
let value_ref = &value;
Expand Down