Skip to content

Commit af4a090

Browse files
authored
feat(queries): show result_id in queries list table (#126)
* feat(queries): add result_id to list table, database_id to detail view - queries list now shows RESULT_ID column alongside query ID, replacing DURATION_MS (still visible in queries get detail view) - SQL truncation reduced from 60 to 40 chars to fit the wider table - QueryRun struct gains database_id field (serde default) so the detail view will surface it automatically once the API starts returning it * feat(queries): show full result_id in queries list table * feat(results): color status and add adaptive columns to results list * feat(queries): add execution time (MS) column to queries list * refactor: consolidate color_status into util, remove duplicate in queries/results --------- Co-authored-by: Eddie A Tejeda <669988+eddietejeda@users.noreply.github.com>
1 parent 4a253d8 commit af4a090

3 files changed

Lines changed: 61 additions & 21 deletions

File tree

src/queries.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::api::ApiClient;
2-
use crossterm::style::{Color, Stylize};
2+
use crossterm::style::Stylize;
33
use serde::{Deserialize, Serialize};
44

55
const SQL_KEYWORDS: &[&str] = &[
@@ -125,6 +125,8 @@ struct QueryRun {
125125
sql_hash: String,
126126
sql_text: String,
127127
result_id: Option<String>,
128+
#[serde(default)]
129+
database_id: Option<String>,
128130
error_message: Option<String>,
129131
warning_message: Option<String>,
130132
trace_id: Option<String>,
@@ -139,16 +141,6 @@ struct ListResponse {
139141
next_cursor: Option<String>,
140142
}
141143

142-
fn color_status(status: &str) -> String {
143-
let color = match status {
144-
"succeeded" => Color::Green,
145-
"failed" => Color::Red,
146-
"running" | "queued" | "pending" => Color::Yellow,
147-
_ => Color::Reset,
148-
};
149-
status.with(color).to_string()
150-
}
151-
152144
fn truncate_sql(sql: &str, max: usize) -> String {
153145
let flat = sql.split_whitespace().collect::<Vec<_>>().join(" ");
154146
if flat.chars().count() <= max {
@@ -191,20 +183,21 @@ pub fn list(
191183
.map(|r| {
192184
vec![
193185
r.id.clone(),
194-
color_status(&r.status),
186+
crate::util::color_status(&r.status),
195187
crate::util::format_date(&r.created_at),
196188
r.execution_time_ms
197189
.map(|ms| ms.to_string())
198190
.unwrap_or_else(|| "-".to_string()),
199191
r.row_count
200192
.map(|n| n.to_string())
201193
.unwrap_or_else(|| "-".to_string()),
202-
truncate_sql(&r.sql_text, 60),
194+
r.result_id.as_deref().unwrap_or("-").to_string(),
195+
truncate_sql(&r.sql_text, 40),
203196
]
204197
})
205198
.collect();
206199
crate::table::print(
207-
&["ID", "STATUS", "CREATED", "DURATION_MS", "ROWS", "SQL"],
200+
&["ID", "STATUS", "CREATED", "MS", "ROWS", "RESULT_ID", "SQL"],
208201
&rows,
209202
);
210203
}
@@ -238,7 +231,7 @@ fn print_detail(r: &QueryRun, format: &str) {
238231
"table" => {
239232
let label = |l: &str| format!("{:<14}", l).dark_grey().to_string();
240233
println!("{}{}", label("id:"), r.id);
241-
println!("{}{}", label("status:"), color_status(&r.status));
234+
println!("{}{}", label("status:"), crate::util::color_status(&r.status));
242235
println!(
243236
"{}{}",
244237
label("created:"),
@@ -259,6 +252,9 @@ fn print_detail(r: &QueryRun, format: &str) {
259252
if let Some(ref id) = r.result_id {
260253
println!("{}{}", label("result id:"), id);
261254
}
255+
if let Some(ref id) = r.database_id {
256+
println!("{}{}", label("database id:"), id);
257+
}
262258
if let Some(ref id) = r.saved_query_id {
263259
let version = r
264260
.saved_query_version

src/results.rs

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
use crate::api::ApiClient;
2+
use crossterm::style::Stylize;
23
use serde::{Deserialize, Serialize};
34

45
#[derive(Deserialize, Serialize)]
56
struct ResultEntry {
67
id: String,
78
status: String,
89
created_at: String,
10+
#[serde(default)]
11+
row_count: Option<u64>,
12+
#[serde(default)]
13+
query_run_id: Option<String>,
14+
#[serde(default)]
15+
expires_at: Option<String>,
916
}
1017

1118
#[derive(Deserialize)]
@@ -29,25 +36,48 @@ pub fn list(workspace_id: &str, limit: Option<u32>, offset: Option<u32>, format:
2936
"yaml" => print!("{}", serde_yaml::to_string(&body.results).unwrap()),
3037
"table" => {
3138
if body.results.is_empty() {
32-
use crossterm::style::Stylize;
3339
eprintln!("{}", "No results found.".dark_grey());
3440
} else {
41+
let has_rows = body.results.iter().any(|r| r.row_count.is_some());
42+
let has_query_run = body.results.iter().any(|r| r.query_run_id.is_some());
43+
let has_expires = body.results.iter().any(|r| r.expires_at.is_some());
44+
3545
let rows: Vec<Vec<String>> = body
3646
.results
3747
.iter()
3848
.map(|r| {
39-
vec![
49+
let mut row = vec![
4050
r.id.clone(),
41-
r.status.clone(),
51+
crate::util::color_status(&r.status),
4252
crate::util::format_date(&r.created_at),
43-
]
53+
];
54+
if has_rows {
55+
row.push(r.row_count.map(|n| n.to_string()).unwrap_or_else(|| "-".to_string()));
56+
}
57+
if has_query_run {
58+
row.push(r.query_run_id.as_deref().unwrap_or("-").to_string());
59+
}
60+
if has_expires {
61+
row.push(
62+
r.expires_at
63+
.as_deref()
64+
.map(|s| crate::util::format_date(s))
65+
.unwrap_or_else(|| "-".to_string()),
66+
);
67+
}
68+
row
4469
})
4570
.collect();
46-
crate::table::print(&["ID", "STATUS", "CREATED AT"], &rows);
71+
72+
let mut headers = vec!["ID", "STATUS", "CREATED"];
73+
if has_rows { headers.push("ROWS"); }
74+
if has_query_run { headers.push("QUERY_RUN_ID"); }
75+
if has_expires { headers.push("EXPIRES"); }
76+
77+
crate::table::print(&headers, &rows);
4778
}
4879
if body.has_more {
4980
let next = offset.unwrap_or(0) + body.count as u32;
50-
use crossterm::style::Stylize;
5181
eprintln!(
5282
"{}",
5383
format!(

src/util.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,20 @@ fn colorize_json_value(v: &str) -> String {
328328
format!("{colored}{comma}")
329329
}
330330

331+
/// Color a status string for terminal output. Covers vocabulary from both
332+
/// query runs (succeeded/failed/running/queued/pending) and results (ready/expired/processing).
333+
pub fn color_status(status: &str) -> String {
334+
use crossterm::style::{Color, Stylize};
335+
let color = match status {
336+
"succeeded" | "ready" => Color::Green,
337+
"failed" => Color::Red,
338+
"running" | "queued" | "pending" | "processing" => Color::Yellow,
339+
"expired" => Color::DarkGrey,
340+
_ => Color::Reset,
341+
};
342+
status.with(color).to_string()
343+
}
344+
331345
/// Format an ISO date string compactly: "2024-03-15 14:23" (no seconds, no timezone).
332346
pub fn format_date(s: &str) -> String {
333347
let s = s.split('.').next().unwrap_or(s).trim_end_matches('Z');

0 commit comments

Comments
 (0)