wasmer_cli/commands/app/secrets/
delete.rs1use crate::{
2 commands::{AsyncCliCommand, app::util::AppIdentFlag},
3 config::WasmerEnv,
4};
5use colored::Colorize;
6use dialoguer::theme::ColorfulTheme;
7use is_terminal::IsTerminal;
8use std::path::{Path, PathBuf};
9use wasmer_backend_api::WasmerClient;
10
11use super::utils::{self, get_secrets};
12
13#[derive(clap::Parser, Debug)]
15pub struct CmdAppSecretsDelete {
16 #[clap(flatten)]
18 pub env: WasmerEnv,
19
20 #[clap(long)]
22 pub quiet: bool,
23
24 #[clap(long, default_value_t = !std::io::stdin().is_terminal())]
26 pub non_interactive: bool,
27
28 #[clap(flatten)]
30 pub app_id: AppIdentFlag,
31
32 #[clap(long = "app-dir", conflicts_with = "app")]
34 pub app_dir_path: Option<PathBuf>,
35
36 #[clap(
38 long,
39 name = "from-file",
40 conflicts_with = "name",
41 conflicts_with = "all"
42 )]
43 pub from_file: Option<PathBuf>,
44
45 #[clap(long, conflicts_with = "name")]
47 pub all: bool,
48
49 #[clap(long)]
51 pub force: bool,
52
53 #[clap(name = "name")]
56 pub secret_name: Option<String>,
57}
58
59impl CmdAppSecretsDelete {
60 fn get_secret_name(&self) -> anyhow::Result<String> {
61 if let Some(name) = &self.secret_name {
62 return Ok(name.clone());
63 }
64
65 if self.non_interactive {
66 anyhow::bail!("No secret name given. Provide one as a positional argument.")
67 } else {
68 let theme = ColorfulTheme::default();
69 Ok(dialoguer::Input::with_theme(&theme)
70 .with_prompt("Enter the name of the secret")
71 .interact_text()?)
72 }
73 }
74
75 async fn delete(
76 &self,
77 client: &WasmerClient,
78 app_id: &str,
79 secret_name: &str,
80 ) -> anyhow::Result<()> {
81 let secret = utils::get_secret_by_name(client, app_id, secret_name).await?;
82
83 if let Some(secret) = secret {
84 if !self.non_interactive && !self.force {
85 let theme = ColorfulTheme::default();
86 let res = dialoguer::Confirm::with_theme(&theme)
87 .with_prompt(format!("Delete secret '{}'?", secret_name.bold()))
88 .interact()?;
89 if !res {
90 return Ok(());
91 }
92 }
93
94 let res = wasmer_backend_api::query::delete_app_secret(client, secret.id.into_inner())
95 .await?;
96
97 match res {
98 Some(res) if !res.success => {
99 anyhow::bail!("Error deleting secret '{}'", secret.name.bold())
100 }
101 Some(_) => {
102 if !self.quiet {
103 eprintln!("Correctly deleted secret '{}'", secret.name.bold());
104 }
105 Ok(())
106 }
107 None => {
108 anyhow::bail!("Error deleting secret '{}'", secret.name.bold())
109 }
110 }
111 } else {
112 if !self.quiet {
113 eprintln!("No secret with name '{}' found", secret_name.bold());
114 }
115 Ok(())
116 }
117 }
118
119 async fn delete_from_file(
120 &self,
121 client: &WasmerClient,
122 path: &Path,
123 app_id: String,
124 ) -> anyhow::Result<()> {
125 let secrets = super::utils::read_secrets_from_file(path).await?;
126
127 for secret in secrets {
128 self.delete(client, &app_id, &secret.name).await?;
129 }
130
131 Ok(())
132 }
133}
134
135#[async_trait::async_trait]
136impl AsyncCliCommand for CmdAppSecretsDelete {
137 type Output = ();
138
139 async fn run_async(self) -> Result<Self::Output, anyhow::Error> {
140 let client = self.env.client()?;
141 let app_id = super::utils::get_app_id(
142 &client,
143 self.app_id.app.as_ref(),
144 self.app_dir_path.as_ref(),
145 self.quiet,
146 self.non_interactive,
147 )
148 .await?;
149 if let Some(file) = &self.from_file {
150 self.delete_from_file(&client, file, app_id).await
151 } else if self.all {
152 if self.non_interactive && !self.force {
153 anyhow::bail!(
154 "Refusing to delete all secrets in non-interactive mode without the `--force` flag."
155 )
156 }
157 let secrets = get_secrets(&client, &app_id).await?;
158 for secret in secrets {
159 self.delete(&client, &app_id, &secret.name).await?;
160 }
161
162 Ok(())
163 } else {
164 let name = self.get_secret_name()?;
165 self.delete(&client, &app_id, &name).await
166 }
167 }
168}