Skip to content

Commit

Permalink
Add explicit option to disable thresholds from CLI
Browse files Browse the repository at this point in the history
Previously, when setting the project thresholds from the CLI, a value of
`0` would automatically disable the specified threshold. This does not
correctly represent the backend configuration, since some thresholds
cannot be disabled and it is possible to have a threshold enabled with a
value of `0`.

To address both of these issues, an explicit `Disabled` option has been
added to the interactive `set-thresholds` command, which allows
controlling the `active` state of the threshold independently from its
value.

To ensure that the "Total Project" threshold is not disabled, a special
case has been added which will inform the user that a value must be set
for this threshold.

Closes #288.
  • Loading branch information
cd-work committed Apr 29, 2022
1 parent 286f77c commit da6d947
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 31 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 14 additions & 12 deletions cli/src/commands/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,10 @@ pub async fn handle_project(api: &mut PhylumApi, matches: &clap::ArgMatches) ->
);
println!();

println!("Specify the thresholds and actions for {}. A threshold of zero will disable the threshold.", format_args!("{}", White.paint(project_name)));
println!(
"Specify the thresholds and actions for {}. Accepted values are 0-100 or 'Disabled'.",
format_args!("{}", White.paint(project_name))
);
println!();

let project_details = match api.get_project_details(project_name).await {
Expand All @@ -140,17 +143,21 @@ pub async fn handle_project(api: &mut PhylumApi, matches: &clap::ArgMatches) ->
}
};

for threshold_name in vec![
for threshold_name in &[
"total project",
"author",
"engineering",
"license",
"malicious code",
"vulnerability",
]
.iter()
{
let (threshold, action) = prompt_threshold(threshold_name).unwrap_or((0, "none"));
] {
let threshold = match prompt_threshold(threshold_name) {
Ok(threshold) => threshold,
Err(_) => {
print_user_failure!("Failed to read user input");
continue;
}
};

// API expects slight key change for specific fields.
let name = match *threshold_name {
Expand All @@ -159,12 +166,7 @@ pub async fn handle_project(api: &mut PhylumApi, matches: &clap::ArgMatches) ->
x => x.to_string(),
};

user_settings.set_threshold(
project_details.id.clone(),
name,
threshold,
action.to_string(),
);
user_settings.set_threshold(project_details.id.clone(), name, threshold);
}

let resp = api.put_user_settings(&user_settings).await;
Expand Down
58 changes: 40 additions & 18 deletions cli/src/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,52 @@ use ansi_term::Color::White;
use dialoguer::theme::ColorfulTheme;
use dialoguer::{Input, Select};

use phylum_types::types::user_settings::Threshold;

/// Project thresholds which cannot be disabled.
const ALWAYS_ENABLED_THRESHOLDS: [&str; 1] = ["total project"];

/// Prompt the user for the threshold value and action associated with a given
/// threshold.
pub fn prompt_threshold(name: &str) -> Result<(i32, &str), std::io::Error> {
pub fn prompt_threshold(name: &str) -> Result<Threshold, std::io::Error> {
let threshold = Input::with_theme(&ColorfulTheme::default())
.with_prompt(format!(
"{} Threshold",
format_args!("{}", White.paint(name.to_uppercase()))
))
.validate_with(|input: &String| -> Result<(), &str> {
if input.chars().all(char::is_numeric) {
.validate_with(|input: &String| -> Result<(), String> {
if input.eq_ignore_ascii_case("disabled") {
if ALWAYS_ENABLED_THRESHOLDS.contains(&name) {
Err(format!("Cannot disable {} threshold", name))
} else {
Ok(())
}
} else if input.chars().all(char::is_numeric) {
let val = input.parse::<i32>().unwrap();
if (0..=100).contains(&val) {
Ok(())
} else {
Err("Make sure to specify a number between 0-100")
Err("Make sure to specify a number between 0-100".into())
}
} else {
Err("Threshold must be a number between 0-100")
Err("Threshold must be a number between 0-100 or 'Disabled'".into())
}
})
.report(true)
.interact_text()?;

if threshold == "0" {
if threshold.eq_ignore_ascii_case("disabled") {
println!(
"\nDisabling {} risk domain",
format_args!("{}", White.paint(name))
);
println!("\n-----\n");
return Ok((0, "none"));

return Ok(Threshold {
action: "none".into(),
threshold: 0.,
active: false,
});
}

println!(
Expand All @@ -55,15 +71,21 @@ pub fn prompt_threshold(name: &str) -> Result<(i32, &str), std::io::Error> {
println!("✔ {} Action · {}", White.paint(name.to_uppercase()), action);
println!("\n-----\n");

Ok((
threshold.parse::<i32>().unwrap(),
match selection {
// Convert the provided selection index into a string suitable for sending
// back to the API endpoint responsible for handling user settings.
0 => "break",
1 => "warn",
2 => "none",
_ => "warn", // We shouldn't be able to make it here.
},
))
let threshold = threshold.parse::<i32>().unwrap() as f32 / 100.;

let action = match selection {
// Convert the provided selection index into a string suitable for sending
// back to the API endpoint responsible for handling user settings.
0 => "break",
1 => "warn",
2 => "none",
_ => "warn", // We shouldn't be able to make it here.
}
.to_owned();

Ok(Threshold {
active: true,
threshold,
action,
})
}

0 comments on commit da6d947

Please sign in to comment.