fraiseql_cli/commands/schema/
metadata.rs1use anyhow::Result;
13
14pub async fn run(server_url: &str, token: Option<&str>) -> Result<()> {
21 let url = format!("{}/api/v1/schema/metadata", server_url.trim_end_matches('/'));
22
23 let client = reqwest::Client::builder().timeout(std::time::Duration::from_secs(10)).build()?;
24
25 let mut req = client.get(&url);
26 if let Some(tok) = token {
27 req = req.header("Authorization", format!("Bearer {tok}"));
28 }
29
30 let resp = req
31 .send()
32 .await
33 .map_err(|e| anyhow::anyhow!("Failed to connect to server at {url}: {e}"))?;
34
35 if !resp.status().is_success() {
36 return Err(anyhow::anyhow!("Server returned HTTP {}", resp.status()));
37 }
38
39 let body: serde_json::Value = resp
40 .json()
41 .await
42 .map_err(|e| anyhow::anyhow!("Failed to parse server response: {e}"))?;
43
44 let metadata = body
45 .pointer("/data/metadata")
46 .ok_or_else(|| anyhow::anyhow!("Unexpected response shape — missing /data/metadata"))?;
47
48 print!("{}", format_table(metadata));
49 Ok(())
50}
51
52pub fn format_table(metadata: &serde_json::Value) -> String {
57 let Some(obj) = metadata.as_object() else {
58 return "No metadata entries found.\n".to_string();
59 };
60
61 if obj.is_empty() {
62 return "No metadata entries found.\n".to_string();
63 }
64
65 let mut rows: Vec<(String, String, String, String)> = obj
67 .iter()
68 .map(|(field, meta)| {
69 let encrypted = if meta.get("encrypted").and_then(|v| v.as_bool()).unwrap_or(false) {
70 "true".to_string()
71 } else {
72 "-".to_string()
73 };
74 let scope =
75 meta.get("requires_scope").and_then(|v| v.as_str()).unwrap_or("-").to_string();
76 let on_deny = meta.get("on_deny").and_then(|v| v.as_str()).unwrap_or("-").to_string();
77 (field.clone(), encrypted, scope, on_deny)
78 })
79 .collect();
80
81 rows.sort_by(|a, b| a.0.cmp(&b.0));
82
83 let w0 = rows.iter().map(|r| r.0.len()).max().unwrap_or(0).max("Field".len());
85 let w1 = rows.iter().map(|r| r.1.len()).max().unwrap_or(0).max("Encrypted".len());
86 let w2 = rows.iter().map(|r| r.2.len()).max().unwrap_or(0).max("Scope".len());
87 let w3 = rows.iter().map(|r| r.3.len()).max().unwrap_or(0).max("On Deny".len());
88
89 let mut out = String::new();
90 out.push_str(&format!(
91 "{:<w0$} {:<w1$} {:<w2$} {:<w3$}\n",
92 "Field", "Encrypted", "Scope", "On Deny"
93 ));
94 out.push_str(&format!("{:-<w0$} {:-<w1$} {:-<w2$} {:-<w3$}\n", "", "", "", ""));
95 for (field, enc, scope, on_deny) in &rows {
96 out.push_str(&format!("{:<w0$} {:<w1$} {:<w2$} {:<w3$}\n", field, enc, scope, on_deny));
97 }
98
99 out
100}