diff --git a/Cargo.lock b/Cargo.lock index 2e278df45a2e2..6688e78f5c61a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12166,6 +12166,7 @@ dependencies = [ "once_cell", "rand 0.8.5", "rocksdb", + "serde_json", "simulacrum", "sui-config", "sui-core", diff --git a/crates/sui-graphql-e2e-tests/tests/call/simple.exp b/crates/sui-graphql-e2e-tests/tests/call/simple.exp index ee4c605251e3c..ba7646f760c5c 100644 --- a/crates/sui-graphql-e2e-tests/tests/call/simple.exp +++ b/crates/sui-graphql-e2e-tests/tests/call/simple.exp @@ -1,4 +1,4 @@ -processed 15 tasks +processed 18 tasks task 1 'publish'. lines 6-28: created: object(1,0) @@ -26,7 +26,7 @@ task 6 'advance-epoch'. lines 39-39: Epoch advanced: 5 task 7 'view-checkpoint'. lines 41-41: -CheckpointSummary { epoch: 5, seq: 10, content_digest: BVsPCtqsoq7tCkDUZeoFQkNsj23Bqjjt8khNPCnAFwRK, +CheckpointSummary { epoch: 5, seq: 10, content_digest: AYWnAH2mnV56mBixSbTBco6dqZRksmeHncHKJEEiAL7w, epoch_rolling_gas_cost_summary: GasCostSummary { computation_cost: 0, storage_cost: 0, storage_rebate: 0, non_refundable_storage_fee: 0 }} task 8 'run-graphql'. lines 43-49: @@ -82,5 +82,97 @@ task 13 'view-checkpoint'. lines 70-70: CheckpointSummary { epoch: 6, seq: 11, content_digest: D3oWLCcqoa1D15gxzvMaDemNNY8YYVspAkYkcmtQKWRt, epoch_rolling_gas_cost_summary: GasCostSummary { computation_cost: 0, storage_cost: 0, storage_rebate: 0, non_refundable_storage_fee: 0 }} -task 14 'advance-epoch'. lines 72-72: +task 14 'advance-epoch'. lines 72-75: Epoch advanced: 6 + +task 15 'run-graphql'. lines 77-90: +Response: { + "data": { + "address": { + "objectConnection": { + "edges": [ + { + "node": { + "location": "0x5dee0c408687d51a12f9edd765733887eb5d784bf38a8511195cf2bc64e3d801", + "digest": "8MCxnZGPBDtTuHSdZJrfEc4nAAmhxAMbXhRdAYYNGi26", + "kind": "OWNED" + } + } + ] + } + } + } +} + +task 16 'run-graphql'. lines 92-116: +Response: { + "data": { + "address": { + "objectConnection": { + "edges": [] + } + }, + "second": { + "objectConnection": { + "edges": [ + { + "node": { + "location": "0x5dee0c408687d51a12f9edd765733887eb5d784bf38a8511195cf2bc64e3d801", + "digest": "8MCxnZGPBDtTuHSdZJrfEc4nAAmhxAMbXhRdAYYNGi26", + "kind": "OWNED" + } + } + ] + } + } + } +} + +task 17 'view-graphql-variables'. lines 119-120: +Name: A +Type: SuiAddress! +Value: "0x42" + +Name: A_opt +Type: SuiAddress +Value: "0x42" + +Name: Test +Type: SuiAddress! +Value: "0xB214F63E7E7CEA32DC57EDBA9C924524F44A7BB50E72D0E102265219D4DE7D0E" + +Name: Test_opt +Type: SuiAddress +Value: "0xB214F63E7E7CEA32DC57EDBA9C924524F44A7BB50E72D0E102265219D4DE7D0E" + +Name: deepbook +Type: SuiAddress! +Value: "0xDEE9" + +Name: deepbook_opt +Type: SuiAddress +Value: "0xDEE9" + +Name: std +Type: SuiAddress! +Value: "0x1" + +Name: std_opt +Type: SuiAddress +Value: "0x1" + +Name: sui +Type: SuiAddress! +Value: "0x2" + +Name: sui_opt +Type: SuiAddress +Value: "0x2" + +Name: sui_system +Type: SuiAddress! +Value: "0x3" + +Name: sui_system_opt +Type: SuiAddress +Value: "0x3" diff --git a/crates/sui-graphql-e2e-tests/tests/call/simple.move b/crates/sui-graphql-e2e-tests/tests/call/simple.move index edf9497300dda..89c310021599a 100644 --- a/crates/sui-graphql-e2e-tests/tests/call/simple.move +++ b/crates/sui-graphql-e2e-tests/tests/call/simple.move @@ -70,3 +70,51 @@ module Test::M1 { //# view-checkpoint //# advance-epoch + +// Demonstrates using variables +// If the variable ends in _opt, this is the optional variant + +//# run-graphql --variables A +{ + address(address: $A) { + objectConnection{ + edges { + node { + location + digest + kind + } + } + } + } +} + +//# run-graphql --variables Test A +{ + address(address: $Test) { + objectConnection{ + edges { + node { + location + digest + kind + } + } + } + } + second: address(address: $A) { + objectConnection{ + edges { + node { + location + digest + kind + } + } + } + } +} + + +//# view-graphql-variables +// List all the graphql variables diff --git a/crates/sui-graphql-rpc/src/client/simple_client.rs b/crates/sui-graphql-rpc/src/client/simple_client.rs index 20f9d0eb81a96..31f8eb9fa0959 100644 --- a/crates/sui-graphql-rpc/src/client/simple_client.rs +++ b/crates/sui-graphql-rpc/src/client/simple_client.rs @@ -11,6 +11,7 @@ use std::collections::BTreeMap; use super::response::GraphqlResponse; +#[derive(Clone, Debug)] pub struct GraphqlQueryVariable { pub name: String, pub ty: String, diff --git a/crates/sui-transactional-test-runner/Cargo.toml b/crates/sui-transactional-test-runner/Cargo.toml index 29de4fd276745..7cb6d8f513a51 100644 --- a/crates/sui-transactional-test-runner/Cargo.toml +++ b/crates/sui-transactional-test-runner/Cargo.toml @@ -18,6 +18,7 @@ rand.workspace = true tempfile.workspace = true async-trait.workspace = true tokio.workspace = true +serde_json.workspace = true fastcrypto.workspace = true move-binary-format.workspace = true diff --git a/crates/sui-transactional-test-runner/src/args.rs b/crates/sui-transactional-test-runner/src/args.rs index 2f8d7c4c31ca2..2311ea3c96337 100644 --- a/crates/sui-transactional-test-runner/src/args.rs +++ b/crates/sui-transactional-test-runner/src/args.rs @@ -144,6 +144,8 @@ pub struct RunGraphqlCommand { pub show_headers: bool, #[clap(long = "show-service-version")] pub show_service_version: bool, + #[clap(long = "variables", num_args(1..))] + pub variables: Vec, } #[derive(Debug, clap::Parser)] @@ -182,6 +184,8 @@ pub enum SuiSubcommand { ViewCheckpoint, #[clap(name = "run-graphql")] RunGraphql(RunGraphqlCommand), + #[clap(name = "view-graphql-variables")] + ViewGraphqlVariables, } #[derive(Clone, Debug)] diff --git a/crates/sui-transactional-test-runner/src/test_adapter.rs b/crates/sui-transactional-test-runner/src/test_adapter.rs index a7e84d30824b4..2ffc97bc238cd 100644 --- a/crates/sui-transactional-test-runner/src/test_adapter.rs +++ b/crates/sui-transactional-test-runner/src/test_adapter.rs @@ -44,6 +44,7 @@ use std::{ use sui_core::authority::test_authority_builder::TestAuthorityBuilder; use sui_core::authority::AuthorityState; use sui_framework::DEFAULT_FRAMEWORK_PATH; +use sui_graphql_rpc::client::simple_client::GraphqlQueryVariable; use sui_graphql_rpc::config::ConnectionConfig; use sui_graphql_rpc::test_infra::cluster::serve_executor; use sui_graphql_rpc::test_infra::cluster::ExecutorCluster; @@ -133,6 +134,7 @@ pub(crate) struct StagedPackage { pub(crate) digest: Vec, } +#[derive(Debug)] struct TestAccount { address: SuiAddress, key_pair: AccountKeyPair, @@ -474,10 +476,24 @@ impl<'a> MoveTestAdapter<'a> for SuiTestAdapter<'a> { }}; } match command { + SuiSubcommand::ViewGraphqlVariables => { + let variables = self.graphql_variables(); + let mut res = vec![]; + for (name, value) in variables { + res.push(format!( + "Name: {}\nType: {}\nValue: {}", + name, + value.ty, + serde_json::to_string_pretty(&value.value).unwrap() + )); + } + Ok(Some(res.join("\n\n"))) + } SuiSubcommand::RunGraphql(RunGraphqlCommand { show_usage, show_headers, show_service_version, + variables, }) => { let file = data.ok_or_else(|| anyhow::anyhow!("Missing GraphQL query"))?; let contents = std::fs::read_to_string(file.path())?; @@ -487,9 +503,15 @@ impl<'a> MoveTestAdapter<'a> for SuiTestAdapter<'a> { .wait_for_checkpoint_catchup(highest_checkpoint, Duration::from_secs(10)) .await; + let used_variables = self.resolve_graphql_variables(&variables)?; let resp = cluster .graphql_client - .execute_to_graphql(contents.trim().to_owned(), show_usage, vec![], vec![]) + .execute_to_graphql( + contents.trim().to_owned(), + show_usage, + used_variables, + vec![], + ) .await?; let mut output = vec![]; @@ -933,6 +955,53 @@ impl<'a> SuiTestAdapter<'a> { self.executor } + fn graphql_variables(&self) -> BTreeMap { + let mut variables = BTreeMap::new(); + for (name, addr) in &self.compiled_state.named_address_mapping { + let addr = addr.to_string(); + + // Required variant + variables.insert( + name.to_owned(), + GraphqlQueryVariable { + name: name.to_string(), + value: serde_json::json!(addr), + ty: "SuiAddress!".to_string(), + }, + ); + // Optional variant + let name = name.to_string() + "_opt"; + variables.insert( + name.clone(), + GraphqlQueryVariable { + name: name.to_string(), + value: serde_json::json!(addr), + ty: "SuiAddress".to_string(), + }, + ); + } + variables + } + fn resolve_graphql_variables( + &self, + declared: &[String], + ) -> anyhow::Result> { + let variables = self.graphql_variables(); + let mut res = vec![]; + for decl in declared { + if let Some(var) = variables.get(decl) { + res.push(var.clone()); + } else { + return Err(anyhow!( + "Unknown variable: {}\nAllowed variable mappings are are {:#?}", + decl, + variables + )); + } + } + Ok(res) + } + async fn upgrade_package( &mut self, before_upgrade: NumericalAddress,