1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use super::utils;
use crate::{
    commands::{app::util::AppIdentFlag, AsyncCliCommand},
    config::WasmerEnv,
    opts::ListFormatOpts,
    utils::render::{ItemFormat, ListFormat},
};
use dialoguer::theme::ColorfulTheme;
use is_terminal::IsTerminal;
use std::path::PathBuf;

/// Reveal the value of an existing app secret.
#[derive(clap::Parser, Debug)]
pub struct CmdAppSecretsReveal {
    /* --- Common flags --- */
    #[clap(flatten)]
    pub env: WasmerEnv,

    /// Don't print any message.
    #[clap(long)]
    pub quiet: bool,

    /// Do not prompt for user input.
    #[clap(long, default_value_t = !std::io::stdin().is_terminal())]
    pub non_interactive: bool,

    #[clap(flatten)]
    pub fmt: Option<ListFormatOpts>,

    /* --- Flags --- */
    #[clap(flatten)]
    pub app_id: AppIdentFlag,

    /// The path to the directory where the config file for the application will be written to.
    #[clap(long = "app-dir", conflicts_with = "app")]
    pub app_dir_path: Option<PathBuf>,

    /// Reveal all the secrets related to an app.
    #[clap(long, conflicts_with = "name")]
    pub all: bool,

    /* --- Parameters --- */
    /// The name of the secret to get the value of.
    #[clap(name = "name")]
    pub secret_name: Option<String>,
}

impl CmdAppSecretsReveal {
    fn get_secret_name(&self) -> anyhow::Result<String> {
        if let Some(name) = &self.secret_name {
            return Ok(name.clone());
        }

        if self.non_interactive {
            anyhow::bail!("No secret name given. Provide one as a positional argument.")
        } else {
            let theme = ColorfulTheme::default();
            Ok(dialoguer::Input::with_theme(&theme)
                .with_prompt("Enter the name of the secret:")
                .interact_text()?)
        }
    }
}

#[async_trait::async_trait]
impl AsyncCliCommand for CmdAppSecretsReveal {
    type Output = ();

    async fn run_async(self) -> Result<Self::Output, anyhow::Error> {
        let client = self.env.client()?;
        let app_id = super::utils::get_app_id(
            &client,
            self.app_id.app.as_ref(),
            self.app_dir_path.as_ref(),
            self.quiet,
            self.non_interactive,
        )
        .await?;

        if !self.all {
            let name = self.get_secret_name()?;

            let value = utils::get_secret_value_by_name(&client, &app_id, &name).await?;

            let secret = utils::Secret { name, value };

            if let Some(fmt) = &self.fmt {
                let fmt = match fmt.format {
                    ListFormat::Json => ItemFormat::Json,
                    ListFormat::Yaml => ItemFormat::Yaml,
                    ListFormat::Table => ItemFormat::Table,
                    ListFormat::ItemTable => {
                        anyhow::bail!("The 'item-table' format is not available for single values.")
                    }
                };
                println!("{}", fmt.render(&secret));
            } else {
                print!("{}", secret.value);
            }
        } else {
            let secrets: Vec<utils::Secret> = utils::reveal_secrets(&client, &app_id).await?;

            if let Some(fmt) = &self.fmt {
                println!("{}", fmt.format.render(secrets.as_slice()));
            } else {
                for secret in secrets {
                    println!(
                        "{}=\"{}\"",
                        secret.name,
                        utils::render::sanitize_value(&secret.value)
                    );
                }
            }
        }

        Ok(())
    }
}