use crate::errors::{SecParError, format_sdk_error};
use crate::opt::{GlobalOpts, ParCommand};
use crate::specs::ParameterStore;
use crate::ui::{
build_parameters_table, confirm_action, confirm_delete, new_spinner, print_aborted,
print_env_context, print_info, print_success, print_value, select_from_list,
};
use aws_sdk_ssm::{Client, types::ParameterType};
use color_eyre::Result;
use tracing::{debug, info};
pub async fn list_parameters(
client: &Client,
) -> Result<Vec<(String, String, String)>, SecParError> {
let mut pars_stream = client.describe_parameters().into_paginator().send();
let mut rows = Vec::new();
while let Some(page) = pars_stream.next().await {
debug!("New Parameter Page!");
let page = page.map_err(|e| SecParError::AwsSdk(e.to_string()))?;
for p in page.parameters() {
let name = p.name().unwrap_or("unknown").to_owned();
let typ = p
.r#type()
.map(|t| t.as_str().to_owned())
.unwrap_or_else(|| "unknown".to_owned());
let last_modified = p
.last_modified_date()
.map(|d| d.to_string())
.unwrap_or_default();
rows.push((name, typ, last_modified));
}
}
Ok(rows)
}
pub async fn get_parameter(client: &Client, name: &str) -> Result<String, SecParError> {
match client
.get_parameter()
.name(name)
.with_decryption(true)
.send()
.await
{
Ok(output) => match output.parameter().and_then(|p| p.value()) {
Some(value) => Ok(value.to_owned()),
None => Err(SecParError::MissingValue(format!(
"parameter value is empty for {name}"
))),
},
Err(e) => {
debug!("Error: {:?}", e.to_string());
Err(SecParError::AwsSdk(format_sdk_error(&e)))
}
}
}
pub async fn create_parameter(client: &Client, name: &str, value: &str) -> Result<(), SecParError> {
match client
.put_parameter()
.overwrite(true)
.r#type(ParameterType::SecureString)
.name(name)
.value(value)
.send()
.await
{
Ok(_) => {
debug!("created");
Ok(())
}
Err(e) => {
debug!("Error: {:?}", e.to_string());
Err(SecParError::AwsSdk(format_sdk_error(&e)))
}
}
}
pub async fn delete_parameter(client: &Client, name: &str) -> Result<(), SecParError> {
match client.delete_parameter().name(name).send().await {
Ok(_) => {
info!("Deleted parameter: {name}");
Ok(())
}
Err(e) => {
debug!("Error: {:?}", e.to_string());
Err(SecParError::AwsSdk(format_sdk_error(&e)))
}
}
}
pub async fn apply_parameters(client: &Client, path: &std::path::Path) -> Result<(), SecParError> {
let spec = ParameterStore::new(path)?;
let total = spec.parameters.len();
for (i, entry) in spec.parameters.iter().enumerate() {
info!("[{}/{}] applying: {}", i + 1, total, entry.name);
create_parameter(client, &entry.name, &entry.value).await?;
}
Ok(())
}
async fn resolve_parameter_name(
client: &Client,
name: Option<&str>,
) -> Result<String, SecParError> {
if let Some(n) = name {
return Ok(n.to_owned());
}
let spinner = new_spinner("Fetching parameters for selection…");
let rows = list_parameters(client).await?;
spinner.finish_and_clear();
let names: Vec<String> = rows.into_iter().map(|(n, _, _)| n).collect();
select_from_list("Select a parameter", &names)
}
pub async fn process_par_command(command: &ParCommand, opts: &GlobalOpts) -> Result<()> {
let shared_config = crate::cli::load_shared_config(opts).await;
let client = Client::new(&shared_config);
match command {
ParCommand::List {} => {
let spinner = new_spinner("🔍 Listing parameters…");
let rows = list_parameters(&client).await?;
spinner.finish_and_clear();
if rows.is_empty() {
print_info("No parameters found.");
} else {
let count = rows.len();
let refs: Vec<(&str, &str, &str)> = rows
.iter()
.map(|(n, t, l)| (n.as_str(), t.as_str(), l.as_str()))
.collect();
println!("{}", build_parameters_table(&refs));
print_info(&format!("{count} parameter(s) found."));
}
}
ParCommand::Get { name } => {
let resolved = resolve_parameter_name(&client, name.as_deref()).await?;
let spinner = new_spinner(&format!("🔍 Retrieving parameter '{resolved}'…"));
let value = get_parameter(&client, &resolved).await?;
spinner.finish_and_clear();
print_value(&resolved, &value);
}
ParCommand::Create { name, value, yes } => {
if !yes {
print_env_context(&crate::cli::env_context(opts));
if !confirm_action(&format!("Create parameter '{name}'?"))? {
print_aborted();
return Ok(());
}
}
let spinner = new_spinner(&format!("✨ Creating parameter '{name}'…"));
create_parameter(&client, name, value).await?;
spinner.finish_and_clear();
print_success(&format!("Parameter '{name}' created."));
}
ParCommand::Delete { name, yes } => {
let resolved = resolve_parameter_name(&client, name.as_deref()).await?;
if !yes {
print_env_context(&crate::cli::env_context(opts));
if !confirm_delete(&resolved)? {
print_aborted();
return Ok(());
}
}
let spinner = new_spinner(&format!("🗑️ Deleting parameter '{resolved}'…"));
delete_parameter(&client, &resolved).await?;
spinner.finish_and_clear();
print_success(&format!("Parameter '{resolved}' deleted."));
}
ParCommand::Apply { path, yes } => {
if !yes {
print_env_context(&crate::cli::env_context(opts));
if !confirm_action(&format!("Apply parameters from '{}'?", path.display()))? {
print_aborted();
return Ok(());
}
}
let spinner = new_spinner(&format!(
"📂 Applying parameters from '{}'…",
path.display()
));
apply_parameters(&client, path).await?;
spinner.finish_and_clear();
print_success("Parameters applied successfully.");
}
}
Ok(())
}