Skip to main content

stmo_cli/commands/
discover.rs

1#![allow(clippy::missing_errors_doc)]
2
3use crate::api::RedashClient;
4use anyhow::Result;
5
6fn format_resource_line(id: u64, name: &str, url: &str, is_draft: bool) -> String {
7    let draft = if is_draft { " [DRAFT]" } else { "" };
8    format!("  {id} - {name}{draft}\n  {url}")
9}
10
11pub async fn discover(client: &RedashClient, search: Option<&str>, limit: usize) -> Result<()> {
12    if let Some(term) = search {
13        discover_by_search(client, term, limit).await
14    } else {
15        discover_own_queries(client).await
16    }
17}
18
19async fn discover_own_queries(client: &RedashClient) -> Result<()> {
20    println!("Fetching your queries from Redash...\n");
21
22    let queries = client.fetch_all_queries().await?;
23
24    println!("=== QUERIES ({}) ===\n", queries.len());
25    for query in &queries {
26        let archived = if query.is_archived { " [ARCHIVED]" } else { "" };
27        let draft = if query.is_draft { " [DRAFT]" } else { "" };
28        println!("  {} - {}{}{}", query.id, query.name, archived, draft);
29    }
30
31    println!("\nUse 'stmo-cli init' to create the queries directory.");
32
33    Ok(())
34}
35
36async fn discover_by_search(client: &RedashClient, term: &str, limit: usize) -> Result<()> {
37    println!("Searching for '{term}'...\n");
38
39    let (queries, dashboards) = tokio::join!(
40        client.search_queries(term, limit),
41        client.search_dashboards(term, limit),
42    );
43    let queries = queries?;
44    let dashboards = dashboards?;
45
46    let base = client.base_url();
47
48    println!("=== QUERIES ({}) ===\n", queries.len());
49    if queries.is_empty() {
50        println!("  No queries found for '{term}'.");
51    } else {
52        for query in &queries {
53            let url = format!("{base}/queries/{}", query.id);
54            println!(
55                "{}",
56                format_resource_line(query.id, &query.name, &url, query.is_draft)
57            );
58        }
59        if queries.len() == limit {
60            println!("\n  (showing first {limit}; raise --limit for more)");
61        }
62    }
63
64    println!("\n=== DASHBOARDS ({}) ===\n", dashboards.len());
65    if dashboards.is_empty() {
66        println!("  No dashboards found for '{term}'.");
67    } else {
68        for dashboard in &dashboards {
69            let url = format!("{base}/dashboard/{}", dashboard.slug);
70            println!(
71                "{}",
72                format_resource_line(dashboard.id, &dashboard.name, &url, dashboard.is_draft)
73            );
74        }
75        if dashboards.len() == limit {
76            println!("\n  (showing first {limit}; raise --limit for more)");
77        }
78    }
79
80    Ok(())
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn format_resource_line_plain() {
89        let line = format_resource_line(
90            42,
91            "My Dashboard",
92            "https://example.com/dashboard/my",
93            false,
94        );
95        assert!(line.contains("42"));
96        assert!(line.contains("My Dashboard"));
97        assert!(line.contains("https://example.com/dashboard/my"));
98        assert!(!line.contains("[DRAFT]"));
99    }
100
101    #[test]
102    fn format_resource_line_draft() {
103        let line = format_resource_line(7, "WIP", "https://example.com/dashboard/wip", true);
104        assert!(line.contains("[DRAFT]"));
105        assert!(line.contains("WIP"));
106    }
107}