mirror of https://github.com/buster-so/buster.git
init added in the postgres onboarding
This commit is contained in:
parent
5ac7fc2dad
commit
efca65932e
|
@ -117,12 +117,15 @@ pub async fn init(destination_path: Option<&str>) -> Result<()> {
|
|||
DatabaseType::Redshift => {
|
||||
setup_redshift(buster_creds.url, buster_creds.api_key, &config_path).await
|
||||
}
|
||||
DatabaseType::Postgres => {
|
||||
setup_postgres(buster_creds.url, buster_creds.api_key, &config_path).await
|
||||
}
|
||||
_ => {
|
||||
println!(
|
||||
"{}",
|
||||
format!("{} support is coming soon!", db_type).yellow()
|
||||
);
|
||||
println!("Currently, only Redshift is supported.");
|
||||
println!("Currently, only Redshift and Postgres are supported.");
|
||||
Err(anyhow::anyhow!("Database type not yet implemented"))
|
||||
}
|
||||
}
|
||||
|
@ -332,6 +335,195 @@ async fn setup_redshift(
|
|||
}
|
||||
}
|
||||
|
||||
async fn setup_postgres(
|
||||
buster_url: String,
|
||||
buster_api_key: String,
|
||||
config_path: &Path,
|
||||
) -> Result<()> {
|
||||
println!("{}", "Setting up PostgreSQL connection...".bold().green());
|
||||
|
||||
// Collect name (with validation)
|
||||
let name_regex = Regex::new(r"^[a-zA-Z0-9_-]+$")?;
|
||||
let name = Text::new("Enter a unique name for this data source:")
|
||||
.with_help_message("Only alphanumeric characters, dash (-) and underscore (_) allowed")
|
||||
.with_validator(move |input: &str| {
|
||||
if input.trim().is_empty() {
|
||||
return Ok(Validation::Invalid("Name cannot be empty".into()));
|
||||
}
|
||||
if name_regex.is_match(input) {
|
||||
Ok(Validation::Valid)
|
||||
} else {
|
||||
Ok(Validation::Invalid(
|
||||
"Name must contain only alphanumeric characters, dash (-) or underscore (_)"
|
||||
.into(),
|
||||
))
|
||||
}
|
||||
})
|
||||
.prompt()?;
|
||||
|
||||
// Collect host
|
||||
let host = Text::new("Enter the PostgreSQL host:")
|
||||
.with_help_message("Example: localhost or db.example.com")
|
||||
.with_validator(|input: &str| {
|
||||
if input.trim().is_empty() {
|
||||
return Ok(Validation::Invalid("Host cannot be empty".into()));
|
||||
}
|
||||
Ok(Validation::Valid)
|
||||
})
|
||||
.prompt()?;
|
||||
|
||||
// Collect port (with validation)
|
||||
let port_str = Text::new("Enter the PostgreSQL port:")
|
||||
.with_default("5432") // Default Postgres port is 5432
|
||||
.with_help_message("Default PostgreSQL port is 5432")
|
||||
.with_validator(|input: &str| match input.parse::<u16>() {
|
||||
Ok(_) => Ok(Validation::Valid),
|
||||
Err(_) => Ok(Validation::Invalid(
|
||||
"Port must be a valid number between 1 and 65535".into(),
|
||||
)),
|
||||
})
|
||||
.prompt()?;
|
||||
let port = port_str.parse::<u16>()?;
|
||||
|
||||
// Collect username
|
||||
let username = Text::new("Enter the PostgreSQL username:")
|
||||
.with_validator(|input: &str| {
|
||||
if input.trim().is_empty() {
|
||||
return Ok(Validation::Invalid("Username cannot be empty".into()));
|
||||
}
|
||||
Ok(Validation::Valid)
|
||||
})
|
||||
.prompt()?;
|
||||
|
||||
// Collect password (masked)
|
||||
let password = Password::new("Enter the PostgreSQL password:")
|
||||
.with_validator(|input: &str| {
|
||||
if input.trim().is_empty() {
|
||||
return Ok(Validation::Invalid("Password cannot be empty".into()));
|
||||
}
|
||||
Ok(Validation::Valid)
|
||||
})
|
||||
.without_confirmation()
|
||||
.prompt()?;
|
||||
|
||||
// Collect database (optional)
|
||||
let database = Text::new("Enter the PostgreSQL database name (optional):")
|
||||
.with_help_message("Leave blank to access all available databases")
|
||||
.prompt()?;
|
||||
let database = if database.trim().is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(database)
|
||||
};
|
||||
|
||||
// Collect schema (optional)
|
||||
let schema = Text::new("Enter the PostgreSQL schema (optional):")
|
||||
.with_help_message("Leave blank to access all available schemas")
|
||||
.with_default("public") // Default Postgres schema is usually 'public'
|
||||
.prompt()?;
|
||||
let schema = if schema.trim().is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(schema)
|
||||
};
|
||||
|
||||
// Show summary and confirm
|
||||
println!("\n{}", "Connection Summary:".bold());
|
||||
println!("Name: {}", name.cyan());
|
||||
println!("Host: {}", host.cyan());
|
||||
println!("Port: {}", port.to_string().cyan());
|
||||
println!("Username: {}", username.cyan());
|
||||
println!("Password: {}", "********".cyan());
|
||||
|
||||
// Display database and schema with clear indication if they're empty
|
||||
if let Some(db) = &database {
|
||||
println!("Database: {}", db.cyan());
|
||||
} else {
|
||||
println!("Database: {}", "All databases (null)".cyan());
|
||||
}
|
||||
|
||||
if let Some(sch) = &schema {
|
||||
println!("Schema: {}", sch.cyan());
|
||||
} else {
|
||||
println!("Schema: {}", "All schemas (null)".cyan());
|
||||
}
|
||||
|
||||
let confirm = Confirm::new("Do you want to create this data source?")
|
||||
.with_default(true)
|
||||
.prompt()?;
|
||||
|
||||
if !confirm {
|
||||
println!("{}", "Data source creation cancelled.".yellow());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Create API request
|
||||
let request = PostDataSourcesRequest {
|
||||
name: name.clone(),
|
||||
env: "dev".to_string(), // Default to dev environment
|
||||
credential: Credential::Postgres(PostgresCredentials {
|
||||
host,
|
||||
port,
|
||||
username,
|
||||
password,
|
||||
database: database.clone().unwrap_or_default(),
|
||||
schema: schema.clone().unwrap_or_default(),
|
||||
jump_host: None,
|
||||
ssh_username: None,
|
||||
ssh_private_key: None,
|
||||
}),
|
||||
};
|
||||
|
||||
// Send to API with progress indicator
|
||||
let spinner = ProgressBar::new_spinner();
|
||||
spinner.set_style(
|
||||
ProgressStyle::default_spinner()
|
||||
.tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ")
|
||||
.template("{spinner:.green} {msg}")
|
||||
.unwrap(),
|
||||
);
|
||||
spinner.set_message("Sending credentials to Buster API...");
|
||||
spinner.enable_steady_tick(Duration::from_millis(100));
|
||||
|
||||
let client = BusterClient::new(buster_url, buster_api_key)?;
|
||||
|
||||
match client.post_data_sources(vec![request]).await {
|
||||
Ok(_) => {
|
||||
spinner.finish_with_message(
|
||||
"✓ Data source created successfully!"
|
||||
.green()
|
||||
.bold()
|
||||
.to_string(),
|
||||
);
|
||||
println!(
|
||||
"\nData source '{}' is now available for use with Buster.",
|
||||
name.cyan()
|
||||
);
|
||||
|
||||
// Create a copy of the values we need for the config file
|
||||
let db_copy = database.clone();
|
||||
let schema_copy = schema.clone();
|
||||
|
||||
// Create buster.yml file
|
||||
create_buster_config_file(
|
||||
config_path,
|
||||
&name,
|
||||
db_copy.as_deref(),
|
||||
schema_copy.as_deref(),
|
||||
)?;
|
||||
|
||||
println!("You can now use this data source with other Buster commands.");
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
spinner.finish_with_message("✗ Failed to create data source".red().bold().to_string());
|
||||
println!("\nError: {}", e);
|
||||
println!("Please check your credentials and try again.");
|
||||
Err(anyhow::anyhow!("Failed to create data source: {}", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to create buster.yml file
|
||||
fn create_buster_config_file(
|
||||
path: &Path,
|
||||
|
|
Loading…
Reference in New Issue