use serde_json::Value;
use crate::gateway::{
STATUS_OP_GATEWAY_AUDIT_LIST, STATUS_OP_GATEWAY_OBSERVE, STATUS_OP_GATEWAY_POLICY_GET,
STATUS_OP_GATEWAY_POLICY_SET, STATUS_OP_GATEWAY_SHUTDOWN, STATUS_SERVICE_NAME,
StatusServiceResponse, build_op_payload_request, build_request, make_udp_service_client,
next_request_id, validate_response,
};
use crate::helpers::{
has_flag, option_value, parse_u64_option, resolve_runtime_endpoint_optional,
};
use super::policy::parse_gateway_policy_set_payload;
use super::DEFAULT_STATUS_BIND_ADDR;
pub(super) fn gateway_observe(args: &[String]) -> Result<(), String> {
let json = has_flag(args, "--json");
let audit_limit = parse_u64_option(args, "--audit-limit", 20)?;
let payload = if option_value(args, "--audit-limit").is_some() {
Some(serde_json::json!({"audit_limit": audit_limit}))
} else {
None
};
let response = call_gateway_op(args, STATUS_OP_GATEWAY_OBSERVE, payload)?;
let result = response
.op_result
.ok_or_else(|| String::from("gateway observe response missing op_result"))?;
if json {
let body = serde_json::json!({
"api_version": "robotrt.gateway.observe.v1",
"kind": "gateway_observe",
"source": response_source(args)?,
"result": result,
});
println!(
"{}",
serde_json::to_string_pretty(&body)
.map_err(|err| format!("serialize gateway observe output failed: {err}"))?
);
} else {
println!("RobotRT Gateway Observe");
println!("source: {}", response_source_label(args)?);
println!(
"policy: {}",
serde_json::to_string(&result["policy"]).unwrap_or_else(|_| String::from("{}"))
);
println!(
"sessions: topic_streams={} action_goals={} mission_queues={}",
result["sessions"]["topic_streams"],
result["sessions"]["action_goals"],
result["sessions"]["mission_queues"],
);
}
Ok(())
}
pub(super) fn gateway_policy_get(args: &[String]) -> Result<(), String> {
let json = has_flag(args, "--json");
let response = call_gateway_op(args, STATUS_OP_GATEWAY_POLICY_GET, None)?;
let result = response
.op_result
.ok_or_else(|| String::from("gateway policy get response missing op_result"))?;
if json {
let body = serde_json::json!({
"api_version": "robotrt.gateway.policy.v1",
"kind": "gateway_policy_get",
"source": response_source(args)?,
"result": result,
});
println!(
"{}",
serde_json::to_string_pretty(&body)
.map_err(|err| format!("serialize gateway policy get output failed: {err}"))?
);
} else {
println!("RobotRT Gateway Policy");
println!("source: {}", response_source_label(args)?);
println!(
"policy: {}",
serde_json::to_string(&result["policy"]).unwrap_or_else(|_| String::from("{}"))
);
}
Ok(())
}
pub(super) fn gateway_policy_set(args: &[String]) -> Result<(), String> {
let json = has_flag(args, "--json");
let payload = parse_gateway_policy_set_payload(args)?;
if payload.is_empty() {
return Err(String::from(
"gateway policy set requires at least one override option",
));
}
let response = call_gateway_op(
args,
STATUS_OP_GATEWAY_POLICY_SET,
Some(Value::Object(payload)),
)?;
let result = response
.op_result
.ok_or_else(|| String::from("gateway policy set response missing op_result"))?;
if json {
let body = serde_json::json!({
"api_version": "robotrt.gateway.policy.v1",
"kind": "gateway_policy_set",
"source": response_source(args)?,
"result": result,
});
println!(
"{}",
serde_json::to_string_pretty(&body)
.map_err(|err| format!("serialize gateway policy set output failed: {err}"))?
);
} else {
println!("RobotRT Gateway Policy Set");
println!("source: {}", response_source_label(args)?);
println!("receipt_id: {}", result["receipt_id"]);
println!(
"policy: {}",
serde_json::to_string(&result["policy"]).unwrap_or_else(|_| String::from("{}"))
);
}
Ok(())
}
pub(super) fn gateway_audit_list(args: &[String]) -> Result<(), String> {
let json = has_flag(args, "--json");
let limit = parse_u64_option(args, "--limit", 20)?;
let mut payload = serde_json::json!({"limit": limit});
if let Some(category) = option_value(args, "--category") {
payload["category"] = Value::String(category);
}
let response = call_gateway_op(args, STATUS_OP_GATEWAY_AUDIT_LIST, Some(payload))?;
let result = response
.op_result
.ok_or_else(|| String::from("gateway audit list response missing op_result"))?;
if json {
let body = serde_json::json!({
"api_version": "robotrt.gateway.audit.v1",
"kind": "gateway_audit_list",
"source": response_source(args)?,
"result": result,
});
println!(
"{}",
serde_json::to_string_pretty(&body)
.map_err(|err| format!("serialize gateway audit list output failed: {err}"))?
);
} else {
println!("RobotRT Gateway Audit");
println!("source: {}", response_source_label(args)?);
println!("entries: {}", result["entries"].as_array().map_or(0, Vec::len));
}
Ok(())
}
pub(super) fn gateway_shutdown(args: &[String]) -> Result<(), String> {
let json = has_flag(args, "--json");
let response = call_gateway_op(args, STATUS_OP_GATEWAY_SHUTDOWN, None)?;
let result = response
.op_result
.ok_or_else(|| String::from("gateway shutdown response missing op_result"))?;
if json {
let body = serde_json::json!({
"api_version": "robotrt.gateway.shutdown.v1",
"kind": "gateway_shutdown",
"source": response_source(args)?,
"result": result,
});
println!(
"{}",
serde_json::to_string_pretty(&body)
.map_err(|err| format!("serialize gateway shutdown output failed: {err}"))?
);
} else {
println!("RobotRT Gateway Shutdown");
println!("source: {}", response_source_label(args)?);
println!("accepted: {}", result["accepted"]);
}
Ok(())
}
fn call_gateway_op(
args: &[String],
op: &str,
payload: Option<Value>,
) -> Result<StatusServiceResponse, String> {
let endpoint = resolve_runtime_endpoint_optional(args, DEFAULT_STATUS_BIND_ADDR)?
.ok_or_else(|| String::from("missing gateway endpoint"))?;
let timeout_ms = parse_u64_option(args, "--timeout-ms", 1000)?;
let client = make_udp_service_client(endpoint.clone(), timeout_ms)?;
let request_id = next_request_id();
let request = payload
.map(|value| build_op_payload_request(op, value))
.unwrap_or_else(|| build_request(op));
let response: StatusServiceResponse = client
.call_json(STATUS_SERVICE_NAME, request_id, &request)
.map_err(|err| format!("gateway op={op} query to {endpoint} failed: {err}"))?;
validate_response(&response, request_id, op)?;
Ok(response)
}
fn response_source(args: &[String]) -> Result<Value, String> {
let endpoint = resolve_runtime_endpoint_optional(args, DEFAULT_STATUS_BIND_ADDR)?
.ok_or_else(|| String::from("missing gateway endpoint"))?;
let timeout_ms = parse_u64_option(args, "--timeout-ms", 1000)?;
Ok(serde_json::json!({
"mode": "remote_service",
"service": STATUS_SERVICE_NAME,
"endpoint": endpoint,
"timeout_ms": timeout_ms,
}))
}
fn response_source_label(args: &[String]) -> Result<String, String> {
let endpoint = resolve_runtime_endpoint_optional(args, DEFAULT_STATUS_BIND_ADDR)?
.ok_or_else(|| String::from("missing gateway endpoint"))?;
Ok(format!("remote:{endpoint}"))
}