227 lines
7.9 KiB
Rust
227 lines
7.9 KiB
Rust
// file: kb_app/src/demo_http.rs
|
|
|
|
//! Tauri commands for the HTTP demo window.
|
|
//!
|
|
//! This module exposes a small manual test surface over the HTTP endpoint pool.
|
|
|
|
use tauri::Manager;
|
|
|
|
/// Request payload for one demo HTTP execution.
|
|
#[derive(Clone, Debug, serde::Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub(crate) struct KbDemoHttpRequest {
|
|
/// Logical role used to select one endpoint from the pool.
|
|
pub role: std::string::String,
|
|
/// JSON-RPC HTTP method name.
|
|
pub method: std::string::String,
|
|
/// Optional first string argument, used by methods such as
|
|
/// `getBalance`, `getAccountInfo`, `getProgramAccounts`,
|
|
/// `getSignaturesForAddress`, `getTransaction`, `sendTransaction`.
|
|
pub first_arg: std::option::Option<std::string::String>,
|
|
/// Optional JSON config payload encoded as a string.
|
|
pub config_json: std::option::Option<std::string::String>,
|
|
}
|
|
|
|
/// Response payload for one demo HTTP execution.
|
|
#[derive(Clone, Debug, serde::Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub(crate) struct KbDemoHttpExecutionPayload {
|
|
/// Selected endpoint name.
|
|
pub endpoint_name: std::string::String,
|
|
/// Selected endpoint provider.
|
|
pub provider: std::string::String,
|
|
/// Selected endpoint URL.
|
|
pub endpoint_url: std::string::String,
|
|
/// Requested role.
|
|
pub role: std::string::String,
|
|
/// Executed method name.
|
|
pub method: std::string::String,
|
|
/// Classified method family.
|
|
pub method_class: std::string::String,
|
|
/// Pretty-printed JSON response payload.
|
|
pub response_json: std::string::String,
|
|
}
|
|
|
|
/// Opens the dedicated HTTP demo window.
|
|
#[tauri::command]
|
|
pub(crate) fn open_demo_http_window(
|
|
app_handle: tauri::AppHandle,
|
|
) -> Result<(), std::string::String> {
|
|
let existing_window_option = app_handle.get_webview_window("demo_http");
|
|
|
|
let demo_window = match existing_window_option {
|
|
Some(demo_window) => demo_window,
|
|
None => {
|
|
let builder = tauri::WebviewWindowBuilder::new(
|
|
&app_handle,
|
|
"demo_http",
|
|
tauri::WebviewUrl::App("demo_http.html".into()),
|
|
)
|
|
.title("Demo Http")
|
|
.inner_size(1400.0, 768.0)
|
|
.min_inner_size(800.0, 600.0)
|
|
.center()
|
|
.visible(true)
|
|
.transparent(false)
|
|
.decorations(true);
|
|
let build_result = builder.build();
|
|
match build_result {
|
|
Ok(window) => window,
|
|
Err(error) => {
|
|
return Err(format!("cannot create demo_http window: {error:?}"));
|
|
}
|
|
}
|
|
}
|
|
};
|
|
let show_result = demo_window.show();
|
|
if let Err(error) = show_result {
|
|
return Err(format!("cannot show demo_http window: {error:?}"));
|
|
}
|
|
let focus_result = demo_window.set_focus();
|
|
if let Err(error) = focus_result {
|
|
return Err(format!("cannot focus demo_http window: {error:?}"));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Returns a fresh snapshot of the HTTP endpoint pool.
|
|
#[tauri::command]
|
|
pub(crate) async fn demo_http_list_pool_clients(
|
|
state: tauri::State<'_, crate::KbAppState>,
|
|
) -> Result<std::vec::Vec<kb_lib::KbHttpPoolClientSnapshot>, std::string::String> {
|
|
Ok(state.http_pool.snapshot().await)
|
|
}
|
|
|
|
/// Executes one manual HTTP request through the endpoint pool.
|
|
#[tauri::command]
|
|
pub(crate) async fn demo_http_execute_request(
|
|
state: tauri::State<'_, crate::KbAppState>,
|
|
request: KbDemoHttpRequest,
|
|
) -> Result<KbDemoHttpExecutionPayload, std::string::String> {
|
|
let role = request.role.trim().to_string();
|
|
if role.is_empty() {
|
|
return Err("demo http role must not be empty".to_string());
|
|
}
|
|
let method = request.method.trim().to_string();
|
|
if method.is_empty() {
|
|
return Err("demo http method must not be empty".to_string());
|
|
}
|
|
let config_json_value_result = kb_parse_optional_demo_http_json(request.config_json);
|
|
let config_json_value = match config_json_value_result {
|
|
Ok(config_json_value) => config_json_value,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let params_result =
|
|
kb_build_demo_http_params(&method, request.first_arg.as_deref(), config_json_value);
|
|
let params = match params_result {
|
|
Ok(params) => params,
|
|
Err(error) => return Err(error),
|
|
};
|
|
let selected_client_result = state
|
|
.http_pool
|
|
.select_client_for_role_and_method(&role, &method)
|
|
.await;
|
|
let selected_client = match selected_client_result {
|
|
Ok(selected_client) => selected_client,
|
|
Err(error) => return Err(error.to_string()),
|
|
};
|
|
let method_class = kb_lib::HttpClient::classify_method(&method);
|
|
let method_class_text = kb_demo_http_method_class_to_string(method_class);
|
|
tracing::info!(
|
|
endpoint_name = %selected_client.endpoint_name(),
|
|
endpoint_url = %selected_client.endpoint_url(),
|
|
role = %role,
|
|
method = %method,
|
|
method_class = %method_class_text,
|
|
"executing demo http request"
|
|
);
|
|
let response_value_result = selected_client
|
|
.execute_json_rpc_result_raw(method.clone(), params)
|
|
.await;
|
|
let response_value = match response_value_result {
|
|
Ok(response_value) => response_value,
|
|
Err(error) => return Err(error.to_string()),
|
|
};
|
|
let response_json_result = serde_json::to_string_pretty(&response_value);
|
|
let response_json = match response_json_result {
|
|
Ok(response_json) => response_json,
|
|
Err(error) => {
|
|
return Err(format!(
|
|
"cannot pretty-print demo http response for method '{}': {}",
|
|
method, error
|
|
));
|
|
}
|
|
};
|
|
Ok(KbDemoHttpExecutionPayload {
|
|
endpoint_name: selected_client.endpoint_name().to_string(),
|
|
provider: selected_client.endpoint_config().provider.clone(),
|
|
endpoint_url: selected_client.endpoint_url().to_string(),
|
|
role,
|
|
method,
|
|
method_class: method_class_text.to_string(),
|
|
response_json,
|
|
})
|
|
}
|
|
|
|
fn kb_parse_optional_demo_http_json(
|
|
config_json: std::option::Option<std::string::String>,
|
|
) -> Result<std::option::Option<serde_json::Value>, std::string::String> {
|
|
let config_json = match config_json {
|
|
Some(config_json) => config_json.trim().to_string(),
|
|
None => {
|
|
return Ok(None);
|
|
}
|
|
};
|
|
if config_json.is_empty() {
|
|
return Ok(None);
|
|
}
|
|
let value_result = serde_json::from_str::<serde_json::Value>(&config_json);
|
|
match value_result {
|
|
Ok(value) => Ok(Some(value)),
|
|
Err(error) => Err(format!("invalid configJson: {}", error)),
|
|
}
|
|
}
|
|
|
|
fn kb_build_demo_http_params(
|
|
method: &str,
|
|
first_arg: std::option::Option<&str>,
|
|
config_json: std::option::Option<serde_json::Value>,
|
|
) -> Result<std::vec::Vec<serde_json::Value>, std::string::String> {
|
|
let needs_first_arg = matches!(
|
|
method,
|
|
"getBalance"
|
|
| "getAccountInfo"
|
|
| "getProgramAccounts"
|
|
| "getSignaturesForAddress"
|
|
| "getTransaction"
|
|
| "sendTransaction"
|
|
);
|
|
if needs_first_arg {
|
|
let first_arg = match first_arg {
|
|
Some(first_arg) => first_arg.trim(),
|
|
None => "",
|
|
};
|
|
if first_arg.is_empty() {
|
|
return Err(format!("method '{}' requires firstArg", method));
|
|
}
|
|
let mut params = vec![serde_json::Value::String(first_arg.to_string())];
|
|
if let Some(config_json) = config_json {
|
|
params.push(config_json);
|
|
}
|
|
return Ok(params);
|
|
}
|
|
let mut params = std::vec::Vec::new();
|
|
if let Some(config_json) = config_json {
|
|
params.push(config_json);
|
|
}
|
|
Ok(params)
|
|
}
|
|
|
|
fn kb_demo_http_method_class_to_string(method_class: kb_lib::KbHttpMethodClass) -> &'static str {
|
|
match method_class {
|
|
kb_lib::KbHttpMethodClass::GeneralRpc => "GeneralRpc",
|
|
kb_lib::KbHttpMethodClass::SendTransaction => "SendTransaction",
|
|
kb_lib::KbHttpMethodClass::HeavyRead => "HeavyRead",
|
|
}
|
|
}
|