Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 163 additions & 55 deletions src/api.rs

Large diffs are not rendered by default.

16 changes: 10 additions & 6 deletions src/commands/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,8 @@ fn find_query_files(query_id: u64) -> Result<Option<(String, String)>> {
}

fn delete_query_files(sql_path: &str, yaml_path: &str) -> Result<()> {
fs::remove_file(sql_path)
.context(format!("Failed to delete {sql_path}"))?;
fs::remove_file(yaml_path)
.context(format!("Failed to delete {yaml_path}"))?;
fs::remove_file(sql_path).context(format!("Failed to delete {sql_path}"))?;
fs::remove_file(yaml_path).context(format!("Failed to delete {yaml_path}"))?;
Ok(())
}

Expand Down Expand Up @@ -117,7 +115,10 @@ pub async fn cleanup(client: &RedashClient) -> Result<()> {
return Ok(());
}

println!("Checking {} queries for archive status...\n", query_ids.len());
println!(
"Checking {} queries for archive status...\n",
query_ids.len()
);

let mut cleaned_count = 0;
let mut errors = Vec::new();
Expand Down Expand Up @@ -182,7 +183,10 @@ pub async fn unarchive(client: &RedashClient, query_ids: Vec<u64>) -> Result<()>
}
}

println!("\n✓ Unarchived {unarchived_count}/{} queries", query_ids.len());
println!(
"\n✓ Unarchived {unarchived_count}/{} queries",
query_ids.len()
);

if !errors.is_empty() {
anyhow::bail!("Failed to unarchive {} queries", errors.len());
Expand Down
288 changes: 164 additions & 124 deletions src/commands/dashboards.rs

Large diffs are not rendered by default.

29 changes: 22 additions & 7 deletions src/commands/datasources.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#![allow(clippy::missing_errors_doc)]

use anyhow::Result;
use super::OutputFormat;
use crate::api::RedashClient;
use crate::models::DataSource;
use super::OutputFormat;
use anyhow::Result;

fn build_status_string(ds: &DataSource) -> String {
let mut status_parts = Vec::new();
Expand Down Expand Up @@ -63,7 +63,10 @@ pub async fn show_data_source(
let ds = client.get_data_source(data_source_id).await?;

let schema = if show_schema {
match client.get_data_source_schema(data_source_id, refresh_schema).await {
match client
.get_data_source_schema(data_source_id, refresh_schema)
.await
{
Ok(s) => Some(s),
Err(e) => {
eprintln!("Error fetching schema: {e:#}");
Expand Down Expand Up @@ -254,10 +257,22 @@ mod tests {

#[test]
fn test_output_format_from_str() {
assert!(matches!("json".parse::<OutputFormat>().unwrap(), OutputFormat::Json));
assert!(matches!("JSON".parse::<OutputFormat>().unwrap(), OutputFormat::Json));
assert!(matches!("table".parse::<OutputFormat>().unwrap(), OutputFormat::Table));
assert!(matches!("TABLE".parse::<OutputFormat>().unwrap(), OutputFormat::Table));
assert!(matches!(
"json".parse::<OutputFormat>().unwrap(),
OutputFormat::Json
));
assert!(matches!(
"JSON".parse::<OutputFormat>().unwrap(),
OutputFormat::Json
));
assert!(matches!(
"table".parse::<OutputFormat>().unwrap(),
OutputFormat::Table
));
assert!(matches!(
"TABLE".parse::<OutputFormat>().unwrap(),
OutputFormat::Table
));
assert!("invalid".parse::<OutputFormat>().is_err());
assert!("csv".parse::<OutputFormat>().is_err());
}
Expand Down
50 changes: 30 additions & 20 deletions src/commands/deploy.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#![allow(clippy::missing_errors_doc)]

use anyhow::{bail, Context, Result};
use crate::api::RedashClient;
use crate::models::Query;
use anyhow::{Context, Result, bail};
use std::collections::HashSet;
use std::fs;
use std::path::Path;
use std::process::Command;
use std::collections::HashSet;
use crate::api::RedashClient;
use crate::models::Query;

fn slugify(s: &str) -> String {
s.to_lowercase()
Expand Down Expand Up @@ -86,8 +86,8 @@ fn get_all_query_metadata() -> Result<Vec<(u64, String)>> {
let path = entry.path();

if path.extension().is_some_and(|ext| ext == "yaml") {
let metadata_content = fs::read_to_string(&path)
.context(format!("Failed to read {}", path.display()))?;
let metadata_content =
fs::read_to_string(&path).context(format!("Failed to read {}", path.display()))?;

let metadata: crate::models::QueryMetadata = serde_yaml::from_str(&metadata_content)
.context(format!("Failed to parse {}", path.display()))?;
Expand Down Expand Up @@ -134,7 +134,10 @@ async fn deploy_visualizations(
description: viz.description.clone(),
};
client.update_visualization(&viz_to_update).await?;
println!(" ✓ Updated visualization: {} (ID: {})", viz_to_update.name, server_viz.id);
println!(
" ✓ Updated visualization: {} (ID: {})",
viz_to_update.name, server_viz.id
);
} else {
let viz_to_create = crate::models::CreateVisualization {
query_id,
Expand All @@ -143,8 +146,13 @@ async fn deploy_visualizations(
options: viz.options.clone(),
description: viz.description.clone(),
};
let created = client.create_visualization(query_id, &viz_to_create).await?;
println!(" ✓ Created visualization: {} (ID: {})", created.name, created.id);
let created = client
.create_visualization(query_id, &viz_to_create)
.await?;
println!(
" ✓ Created visualization: {} (ID: {})",
created.name, created.id
);
}
}
}
Expand Down Expand Up @@ -215,11 +223,10 @@ pub async fn deploy(client: &RedashClient, query_ids: Vec<u64>, all: bool) -> Re
bail!("Query metadata file not found: {yaml_path}");
}

let sql = fs::read_to_string(&sql_path)
.context(format!("Failed to read {sql_path}"))?;
let sql = fs::read_to_string(&sql_path).context(format!("Failed to read {sql_path}"))?;

let metadata_content = fs::read_to_string(&yaml_path)
.context(format!("Failed to read {yaml_path}"))?;
let metadata_content =
fs::read_to_string(&yaml_path).context(format!("Failed to read {yaml_path}"))?;

let metadata: crate::models::QueryMetadata = serde_yaml::from_str(&metadata_content)
.context(format!("Failed to parse {yaml_path}"))?;
Expand Down Expand Up @@ -265,10 +272,8 @@ pub async fn deploy(client: &RedashClient, query_ids: Vec<u64>, all: bool) -> Re
.context("Failed to serialize query metadata")?;
fs::write(format!("{new_base}.yaml"), yaml_content)
.context(format!("Failed to write {new_base}.yaml"))?;
fs::remove_file(&sql_path)
.context(format!("Failed to delete {sql_path}"))?;
fs::remove_file(&yaml_path)
.context(format!("Failed to delete {yaml_path}"))?;
fs::remove_file(&sql_path).context(format!("Failed to delete {sql_path}"))?;
fs::remove_file(&yaml_path).context(format!("Failed to delete {yaml_path}"))?;
println!(" ✓ Created new query: {} - {name}", fetched.id);
println!(" Renamed: 0-{slug}.* → {}-{new_slug}.*", fetched.id);
fetched
Expand Down Expand Up @@ -310,13 +315,18 @@ pub async fn deploy(client: &RedashClient, query_ids: Vec<u64>, all: bool) -> Re
};
let yaml_content = serde_yaml::to_string(&updated_metadata)
.context("Failed to serialize query metadata")?;
fs::write(&yaml_path, yaml_content)
.context(format!("Failed to write {yaml_path}"))?;
fs::write(&yaml_path, yaml_content).context(format!("Failed to write {yaml_path}"))?;
println!(" ✓ {id} - {name}");
result
};

deploy_visualizations(client, result_query.id, &metadata.visualizations, &result_query.visualizations).await?;
deploy_visualizations(
client,
result_query.id,
&metadata.visualizations,
&result_query.visualizations,
)
.await?;
}

println!("\n✓ All resources deployed successfully");
Expand Down
2 changes: 1 addition & 1 deletion src/commands/discover.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![allow(clippy::missing_errors_doc)]

use anyhow::Result;
use crate::api::RedashClient;
use anyhow::Result;

pub async fn discover(client: &RedashClient) -> Result<()> {
println!("Fetching your queries from Redash...\n");
Expand Down
Loading
Loading