Skip to content

Commit

Permalink
[suiop] add point of contact selection for incident review (#19329)
Browse files Browse the repository at this point in the history
## Description 

- Refactor api logic into new module
- get all users so we can select them to be points of contact
- add multiselect to allow selection of an arbitrary set of users
- generalize serde logic for caching api responses (once per day)
- improve tagging of channels to link to the actual channel
- tag associated poc users for an incident

## Test plan 

e2e functional (see [example
message](https://mysten-labs.slack.com/archives/C03QJ1B6BC0/p1726147679829409))

---

## Release notes

Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.

For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.

- [ ] Protocol: 
- [ ] Nodes (Validators and Full nodes): 
- [ ] Indexer: 
- [ ] JSON-RPC: 
- [ ] GraphQL: 
- [ ] CLI: 
- [ ] Rust SDK:
- [ ] REST API:
  • Loading branch information
after-ephemera authored Sep 12, 2024
1 parent ba035ce commit 363eb01
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 141 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions crates/suiop-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ toml_edit.workspace = true
tracing-subscriber.workspace = true
tracing.workspace = true
once_cell.workspace = true
futures.workspace = true
strsim = "0.11.1"

[dev-dependencies]
Expand Down
1 change: 1 addition & 0 deletions crates/suiop-cli/src/cli/incidents/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
mod jira;
mod pd;
mod slack;
mod slack_api;

use anyhow::Result;
use chrono::{Duration, Local};
Expand Down
95 changes: 69 additions & 26 deletions crates/suiop-cli/src/cli/incidents/pd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use anyhow::Result;
use chrono::Utc;
use chrono::{DateTime, Local, NaiveDateTime};
use colored::{ColoredString, Colorize};
use inquire::Confirm;
use inquire::{Confirm, MultiSelect};
use reqwest;
use reqwest::header::HeaderMap;
use reqwest::header::ACCEPT;
Expand All @@ -17,17 +17,28 @@ use std::env;
use strsim::normalized_damerau_levenshtein;
use tracing::debug;

use crate::cli::incidents::slack::Channel;
use crate::cli::incidents::slack::Slack;
use crate::cli::lib::utils::day_of_week;
use crate::DEBUG_MODE;

use super::slack_api::{Channel, User};

#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct Priority {
pub name: String,
id: String,
color: String,
}

impl Priority {
pub fn u8(&self) -> u8 {
self.name
.trim_start_matches("P")
.parse()
.expect("Parsing priority")
}
}

#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct Incident {
#[serde(rename = "incident_number")]
Expand All @@ -36,6 +47,9 @@ pub struct Incident {
created_at: Option<String>,
resolved_at: Option<String>,
html_url: String,
/// The slack users responsible for reporting
#[serde(skip_deserializing)]
poc_users: Option<Vec<User>>,
pub priority: Option<Priority>,
pub slack_channel: Option<Channel>,
}
Expand Down Expand Up @@ -126,7 +140,7 @@ impl Incident {
format!(
"• {} {} {} {}",
if let Some(channel) = self.slack_channel.clone() {
format!("{} ({})", self.number, channel.url())
format!("{} (<#{}>)", self.number, channel.id)
} else {
self.number.to_string()
},
Expand All @@ -138,10 +152,14 @@ impl Incident {
.to_string())
.unwrap_or("".to_owned()),
self.title,
self.slack_channel
.clone()
.map(|c| c.name)
.unwrap_or("".to_string()),
self.poc_users.as_ref().map_or_else(
|| "".to_string(),
|u| u
.iter()
.map(|u| { format!("<@{}>", u.id) })
.collect::<Vec<_>>()
.join(", ")
)
)
}
}
Expand Down Expand Up @@ -251,37 +269,55 @@ pub async fn print_recent_incidents(
Ok(())
}

pub async fn review_recent_incidents(incidents: Vec<Incident>) -> Result<()> {
let incidents_grouped = incidents
/// Filter incidents based on whether they have <= min_priority priority or any slack
/// channel associated.
fn filter_incidents_for_review(incidents: Vec<Incident>, min_priority: &str) -> Vec<Incident> {
let min_priority_u = min_priority
.trim_start_matches("P")
.parse::<u8>()
.expect("Parsing priority");
println!("min_priority_u: {}", min_priority_u);
incidents
.into_iter()
// filter on priority > P3 and any slack channel association
// filter on priority <= min_priority and any slack channel association
.filter(|i| {
i.priority
.clone()
.filter(|p| !p.name.is_empty() && p.name != "P3")
.filter(|p| {
println!("{} <= {}?", p.u8(), min_priority_u);
!p.name.is_empty() && p.u8() <= min_priority_u
})
.is_some()
|| i.slack_channel.is_some()
})
.collect();
let group_map = group_by_similar_title(incidents_grouped, 0.9);
.collect()
}

fn request_pocs(slack: &Slack) -> Result<Vec<User>> {
MultiSelect::new(
"Please select the users who are POCs for this incident",
slack.users.clone(),
)
.with_default(&[])
.prompt()
.map_err(|e| anyhow::anyhow!(e))
}

pub async fn review_recent_incidents(incidents: Vec<Incident>) -> Result<()> {
let slack = Slack::new().await;
let filtered_incidents = filter_incidents_for_review(incidents, "P2");
let mut group_map = group_by_similar_title(filtered_incidents, 0.9);
let mut to_review = vec![];
let mut excluded = vec![];
debug!(
"map: {:#?}",
group_map
.iter()
.map(|(k, v)| (k, v.len()))
.collect::<Vec<_>>()
);
for (title, incident_group) in group_map.iter() {
for (title, incident_group) in group_map.iter_mut() {
let treat_as_one = if incident_group.len() > 1 {
println!(
"There are {} incidents with a title similar to this: {}",
&incident_group.len(),
title
);
println!("All incidents with a similar title:");
for i in incident_group {
for i in incident_group.iter() {
i.print(false)?;
}
Confirm::new("Treat them as one?")
Expand All @@ -297,20 +333,24 @@ pub async fn review_recent_incidents(incidents: Vec<Incident>) -> Result<()> {
.prompt()
.expect("Unexpected response");
if ans {
println!("{:?} added to review", to_review);
let poc_users = request_pocs(&slack)?;
incident_group
.iter_mut()
.for_each(|i| i.poc_users = Some(poc_users.clone()));
to_review.extend(incident_group.clone());
println!("{:?} added to review", to_review);
} else {
excluded.extend(incident_group.clone());
}
} else {
for incident in incident_group.iter() {
for incident in incident_group.iter_mut() {
incident.print(false)?;
let ans = Confirm::new("Keep this incident for review?")
.with_default(false)
.prompt()
.expect("Unexpected response");
if ans {
let poc_users = request_pocs(&slack)?;
incident.poc_users = Some(poc_users.clone());
to_review.push(incident.clone());
} else {
excluded.push(incident.clone());
Expand Down Expand Up @@ -369,7 +409,6 @@ Please comment in the thread to request an adjustment to the list.",
.prompt()
.expect("Unexpected response");
if ans {
let slack = super::slack::Slack::new().await;
slack.send_message(slack_channel, &message).await?;
}
// post to https://slack.com/api/chat.postMessage with message
Expand Down Expand Up @@ -411,6 +450,10 @@ fn group_by_similar_title(
}
}

debug!(
"map: {:#?}",
groups.iter().map(|(k, v)| (k, v.len())).collect::<Vec<_>>()
);
groups
}

Expand Down
Loading

0 comments on commit 363eb01

Please sign in to comment.