-
Notifications
You must be signed in to change notification settings - Fork 30
feat: added check for invalid username and if it is invalid find related names #200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -1,6 +1,7 @@ | ||||
use anyhow::Context; | ||||
use octocrab::models::{App, Repository}; | ||||
use octocrab::{Error, Octocrab}; | ||||
use strsim::levenshtein; | ||||
use tracing::log; | ||||
|
||||
use crate::bors::event::PullRequestComment; | ||||
|
@@ -287,6 +288,72 @@ impl GithubRepositoryClient { | |||
run_ids.map(|workflow_id| self.get_workflow_url(workflow_id)) | ||||
} | ||||
|
||||
/// Validates a reviewer's GitHub username and returns validation results if the username is invalid. | ||||
/// If multiple reviewers are provided (comma-separated), validates each one. | ||||
/// Returns a validation result for the first invalid username encountered, or None if all usernames are valid. | ||||
pub async fn validate_reviewers(&self, reviewer_list: &str) -> anyhow::Result<Option<Comment>> { | ||||
if reviewer_list.trim().is_empty() { | ||||
return Ok(Some(Comment::new( | ||||
"Error: No reviewer specified. Use r=username to specify a reviewer.".to_string(), | ||||
))); | ||||
} | ||||
|
||||
for username in reviewer_list.split(',').map(|s| s.trim()) { | ||||
if username.is_empty() { | ||||
return Ok(Some(Comment::new( | ||||
"Error: Empty reviewer name provided. Use r=username to specify a reviewer." | ||||
.to_string(), | ||||
))); | ||||
} | ||||
|
||||
match self.client.users(username).profile().await { | ||||
Ok(_) => continue, // user exist continue | ||||
Err(octocrab::Error::GitHub { source, .. }) => { | ||||
if source.message.contains("Not Found") { | ||||
let similar_usernames = self.find_similar_usernames(username).await?; | ||||
|
||||
let mut message = format!("Invalid reviewer username: `{}`", username); | ||||
if !similar_usernames.is_empty() { | ||||
message.push_str("\nDid you mean one of these users?\n"); | ||||
for similar in similar_usernames { | ||||
message.push_str(&format!("- {}\n", similar)); | ||||
} | ||||
} | ||||
|
||||
return Ok(Some(Comment::new(message))); | ||||
} | ||||
} | ||||
Err(_) => continue, | ||||
} | ||||
} | ||||
Ok(None) | ||||
} | ||||
|
||||
/// Searches for GitHub usernames similar to the provided username using Levenshtein distance. | ||||
async fn find_similar_usernames(&self, username: &str) -> anyhow::Result<Vec<String>> { | ||||
const MAX_DISTANCE: usize = 2; | ||||
|
||||
let search_results = self | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't use the GitHub search API for this, that could be slow, and unknown GH users are not relevant anyway. Instead, we should try to search for known users in the team database. It will also be easier to test it like this. But we can also skip the "find similar" functionality in this PR, if you want. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
so we should use this right ? Line 51 in 417c8e7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, the reviewer list from the team API. |
||||
.client | ||||
.search() | ||||
.users(username) | ||||
.send() | ||||
.await | ||||
.context("Failed to search for similar usernames")?; | ||||
|
||||
let similar_usernames = search_results | ||||
.items | ||||
.into_iter() | ||||
.filter(|user| { | ||||
let distance = levenshtein(username, &user.login); | ||||
distance > 0 && distance <= MAX_DISTANCE | ||||
}) | ||||
.map(|user| user.login) | ||||
.collect(); | ||||
|
||||
Ok(similar_usernames) | ||||
} | ||||
|
||||
fn format_pr(&self, pr: PullRequestNumber) -> String { | ||||
format!("{}/{}", self.repository(), pr) | ||||
} | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
strtim
dependency is missing.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh shit, forgotten to push it, done now !