forklaunch 1.20.1

Launch faster with forklaunch
use std::io::Write;

use anyhow::{Context, Result, bail};
use clap::{Arg, ArgMatches, Command};
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

use crate::{
    CliCommand,
    constants::{ERROR_FAILED_TO_SEND_REQUEST, get_platform_management_api_url},
    core::{command::command, http_client, validate::require_auth},
};

#[derive(Debug)]
pub(crate) struct LinkCommand;

impl LinkCommand {
    pub(crate) fn new() -> Self {
        Self
    }
}

impl CliCommand for LinkCommand {
    fn command(&self) -> Command {
        command(
            "link",
            "Complete a cloud account link by providing the IAM role ARN",
        )
        .arg(
            Arg::new("id")
                .required(true)
                .help("The cloud account id (from `fl cloud-account create`)"),
        )
        .arg(
            Arg::new("role_arn")
                .long("role-arn")
                .required(true)
                .help("The IAM role ARN created in your AWS account"),
        )
    }

    fn handler(&self, matches: &ArgMatches) -> Result<()> {
        let _token = require_auth()?;
        let id = matches.get_one::<String>("id").context("id is required")?;
        let role_arn = matches
            .get_one::<String>("role_arn")
            .context("--role-arn is required")?;

        let url = format!("{}/cloud-accounts/{}", get_platform_management_api_url(), urlencoding::encode(id));
        let body = serde_json::json!({ "roleArn": role_arn });
        let response =
            http_client::put(&url, body).with_context(|| ERROR_FAILED_TO_SEND_REQUEST)?;

        if !response.status().is_success() {
            bail!(
                "Failed to link cloud account: {}",
                response.text().unwrap_or_default()
            );
        }

        let mut stdout = StandardStream::stdout(ColorChoice::Always);
        stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
        writeln!(stdout, "  Linked")?;
        stdout.reset()?;

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn link_cmd() -> Command {
        LinkCommand::new().command().version("0.0.0-test")
    }

    #[test]
    fn command_definition_is_valid() {
        link_cmd().debug_assert();
    }

    #[test]
    fn requires_id_and_role_arn() {
        assert!(link_cmd().try_get_matches_from(["link"]).is_err());
        assert!(
            link_cmd()
                .try_get_matches_from(["link", "ca-1", "--role-arn", "arn:aws:iam::123:role/x"])
                .is_ok()
        );
    }
}