Skip to content

Commit

Permalink
Merge pull request #29 from Alokit-Innovations/tr/testAuthorFix
Browse files Browse the repository at this point in the history
Chrome extension author display name fix
  • Loading branch information
avikalpg authored Oct 26, 2023
2 parents 98cbf40 + 9392585 commit 53f3ff2
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 107 deletions.
71 changes: 28 additions & 43 deletions vibi-dpu/src/bitbucket/reviewer.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,36 @@
use reqwest::{Response, header::{HeaderMap, HeaderValue}};
use serde::Serialize;
use reqwest::Response;
use serde_json::Value;
use reqwest::Error;
use std::io;

use crate::db::user::get_workspace_user_from_db;
use crate::utils::review::Review;
use crate::utils::user::BitbucketUser;

use super::{config::{bitbucket_base_url, get_client, prepare_headers}};
use super::config::{get_client, prepare_headers};

pub async fn add_reviewers(user_key: &str, review: &Review, access_token: &str) {
pub async fn add_reviewers(user: &BitbucketUser, review: &Review, access_token: &str) {
let url = prepare_get_prinfo_url(review.repo_owner(), review.repo_name(), review.id());
let get_response = get_pr_info(&url, access_token).await;
let reviewers_opt = add_user_to_reviewers(get_response, user_key).await;
let pr_info = get_pr_info(&url, access_token).await;
let reviewers_opt = get_updated_reviewers_vec(pr_info, user).await;
if reviewers_opt.is_none() {
eprintln!("[add_reviewers] Unable to add reviewers for review: {}", review.id());
return;
}
let (reviewers, pr_info_json) = reviewers_opt.expect("Empty reviewers_opt");
let (reviewers, pr_info_json) = reviewers_opt.expect("Empty reviewers_opt");
let put_payload = prepare_put_body(&reviewers, &pr_info_json);
put_reviewers(&url, access_token, &put_payload).await;
}

async fn add_user_to_reviewers(response_res: Option<Response>, user_key: &str) -> Option<(Vec<BitbucketUser>, Value)> {
let reviewers_opt = parse_reviewers_from_prinfo(response_res).await;
async fn get_updated_reviewers_vec(pr_opt: Option<Response>, user_from_db: &BitbucketUser) -> Option<(Vec<BitbucketUser>, Value)> {
let reviewers_opt = parse_reviewers_from_prinfo(pr_opt).await;
if reviewers_opt.is_none() {
eprintln!("Unable to parse and add reviewers");
return None;
}
let (mut reviewers, get_response_json) = reviewers_opt.expect("Empty reviewers_opt");
println!("reviewers = {:?}", reviewers);
// Get user from db who needs to be added to reviewers
let user_from_db_opt = get_workspace_user_from_db(&user_key);
if user_from_db_opt.is_none() {
eprintln!("Empty user_from_db_opt");
return None;
}
let user_from_db = user_from_db_opt.expect("empty user_from_db_opt");
println!("user_from_db = {:?}", &user_from_db);
// For each user in user_from_db.users()...
for user in user_from_db.users().iter() {
// If the reviewers vector doesn't contain the user...
if !reviewers.contains(user) {
// Add the user to reviewers
reviewers.push(user.clone());
}
if !reviewers.contains(user_from_db) {
// Add the user to reviewers
reviewers.push(user_from_db.clone());
}
println!("Updated reviewers = {:?}", reviewers);
return Some((reviewers, get_response_json));
Expand All @@ -68,27 +54,27 @@ fn prepare_put_body(updated_reviewers: &Vec<BitbucketUser>, pr_info_json: &Value
// Update obj
let obj = obj_opt.expect("empty obj_opt");
obj.insert("reviewers".to_string(), reviewers_obj);
obj.remove("summary"); // API gives error if not removed
obj.remove("summary"); // API gives error if not removed
return Some(response_json);
}

async fn parse_reviewers_from_prinfo(response_res: Option<Response>) -> Option<(Vec<BitbucketUser>, Value)>{
if response_res.is_none() {
async fn parse_reviewers_from_prinfo(pr_opt: Option<Response>) -> Option<(Vec<BitbucketUser>, Value)> {
if pr_opt.is_none() {
eprintln!("Empty get response for pr_info");
return None;
}
let get_response = response_res.expect("Error in getting response");
println!("get API status: {}", get_response.status());
let response_json_res = get_response.json::<Value>().await;
if response_json_res.is_err() {
let e = response_json_res.expect_err("No error in response_json_res");
let pr_info_response = pr_opt.expect("Error in getting response");
println!("get API status: {}", pr_info_response.status());
let pr_info_json = pr_info_response.json::<Value>().await;
if pr_info_json.is_err() {
let e = pr_info_json.expect_err("No error in pr_info_json");
eprintln!("Unable to deserialize response_json: {:?}", e);
return None;
}
let response_json = response_json_res.expect("Uncaught error in response_json_res");
let reviewers_opt = response_json.get("reviewers");
let pr_info = pr_info_json.expect("Uncaught error in pr_info_json");
let reviewers_opt = pr_info.get("reviewers");
if reviewers_opt.is_none() {
eprintln!("No reviewers found in response: {:?}", &response_json);
eprintln!("No reviewers found in response: {:?}", &pr_info);
return None;
}
let reviewers_value = reviewers_opt.expect("Empty reviewers_opt").to_owned();
Expand All @@ -99,10 +85,10 @@ async fn parse_reviewers_from_prinfo(response_res: Option<Response>) -> Option<(
return None;
}
let reviewers: Vec<BitbucketUser> = reviewers_res.expect("Uncaught error in response_res");
return Some((reviewers, response_json));
return Some((reviewers, pr_info));
}

async fn put_reviewers(url: &str, access_token: &str,put_body_opt: &Option<Value>) {
async fn put_reviewers(url: &str, access_token: &str, put_body_opt: &Option<Value>) {
if put_body_opt.is_none() {
eprintln!("Empty put request body, not adding reviewers");
return;
Expand All @@ -123,7 +109,7 @@ async fn put_reviewers(url: &str, access_token: &str,put_body_opt: &Option<Value
// for debugging
match response_res {
Ok(v) => println!("response v = {:?}", v.text().await),
Err(e) => println!("response err = {:?}", e)
Err(e) => println!("response err = {:?}", e),
};
}

Expand All @@ -145,8 +131,7 @@ async fn get_pr_info(url: &str, access_token: &str) -> Option<Response> {
return Some(get_response);
}

fn prepare_get_prinfo_url(repo_owner: &str,
repo_name: &str, review_id: &str) -> String {
fn prepare_get_prinfo_url(repo_owner: &str, repo_name: &str, review_id: &str) -> String {
let url = format!(
"{}/repositories/{}/{}/pullrequests/{}",
"https://api.bitbucket.org/2.0".to_string(),
Expand All @@ -156,4 +141,4 @@ fn prepare_get_prinfo_url(repo_owner: &str,
);
println!("add reviews url = {}", &url);
return url;
}
}
43 changes: 9 additions & 34 deletions vibi-dpu/src/bitbucket/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::db::auth::auth_info;
use crate::db::user::{add_bitbucket_user_to_workspace_user, get_workspace_user_from_db};
use crate::utils::auth::AuthInfo;
use crate::utils::lineitem::LineItem;
use crate::utils::user::BitbucketUser;
use crate::utils::user::{BitbucketUser, User, Provider, ProviderEnum};
use super::config::{bitbucket_base_url, get_api_values, get_api_response};

pub async fn get_and_save_workspace_users(workspace_id: &str, access_token: &str) {
Expand All @@ -18,7 +18,7 @@ pub async fn get_and_save_workspace_users(workspace_id: &str, access_token: &str
}
}

pub async fn get_commit_bb(commit: &str, repo_name: &str, repo_owner: &str) -> Option<LineItem>{
pub async fn author_from_commit(commit: &str, repo_name: &str, repo_owner: &str) -> Option<BitbucketUser>{
let base_url = bitbucket_base_url();
let commits_url = format!("{}/repositories/{}/{}/commit/{}", &base_url, repo_owner, repo_name, commit);
println!("commits url = {}", &commits_url);
Expand All @@ -40,38 +40,13 @@ pub async fn get_commit_bb(commit: &str, repo_name: &str, repo_owner: &str) -> O
return None;
}
let response_json = parse_res.expect("Uncaught error in parse_res");
let timestamp_str = &response_json["date"].to_string().replace('"', "");
println!("timestamp_str = {}", timestamp_str);
// Explicitly specify the format
let datetime_res = DateTime::parse_from_rfc3339(&timestamp_str);
if datetime_res.is_err() {
let e = datetime_res.expect_err("No error in dateime_res");
eprintln!("Failed to parse timestamp: {:?}", e);
let author_val = response_json["author"]["user"].to_owned();
let author_res = serde_json::from_value(author_val);
if author_res.is_err() {
let err = author_res.expect_err("Empty error in author_res");
eprintln!("[author_from_commit] Unable to deserialize author: {:?}", err);
return None;
}
let datetime: DateTime<FixedOffset> = datetime_res.expect("Uncaught error in datetime_res");
// Convert to Utc
let datetime_utc = datetime.with_timezone(&Utc);

let unix_timestamp = datetime_utc.timestamp();
let unix_timestamp_str = unix_timestamp.to_string();
let author_id = response_json["author"]["user"]["uuid"].to_string().replace('"', "");
let author_display_name = response_json["author"]["user"]["display_name"].to_string().replace('"', "");
let user_res = response_json["author"].get("user");
if user_res.is_none() {
eprintln!("no user in response_json: {:?}", &response_json);
return None;
}
let bitbucket_user_json = user_res.expect("empty user_res").to_owned();
let bitbucket_user = serde_json::from_value::<BitbucketUser>(bitbucket_user_json)
.expect("error in deserializing BitbucketUser from bitbucket_user_json");
let mut user_key = author_display_name.clone();
let user_opt = get_workspace_user_from_db(&user_key);
if user_opt.is_none() {
eprintln!("No user found in db for key: {}", &user_key);
return Some(LineItem::new(bitbucket_user.display_name().to_owned(), unix_timestamp_str));
}
let user = user_opt.expect("empty user_opt");
user_key = user.display_name().to_owned();
return Some(LineItem::new(user_key, unix_timestamp_str));
let author: BitbucketUser = author_res.expect("Uncaught error in author_res");
return Some(author);
}
21 changes: 13 additions & 8 deletions vibi-dpu/src/core/coverage.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::{HashMap, HashSet};

use crate::{utils::hunk::{HunkMap, PrHunkItem}, db::user::get_workspace_user_from_db, bitbucket::{comment::add_comment, reviewer::add_reviewers}};
use crate::{utils::{hunk::{HunkMap, PrHunkItem}, user::{BitbucketUser, WorkspaceUser}}, db::user::{get_workspace_user_from_db}, bitbucket::{user::author_from_commit, comment::add_comment, reviewer::add_reviewers}};
use crate::utils::review::Review;
use crate::utils::repo_config::RepoConfig;
use crate::bitbucket::auth::get_access_token_review;
Expand All @@ -25,20 +25,25 @@ pub async fn process_coverage(hunkmap: &HunkMap, review: &Review, repo_config: &
// create comment text
let comment = comment_text(coverage_map, repo_config.auto_assign());
// add comment
add_comment(&comment, review, &access_token).await;
add_comment(&comment, review, &access_token).await;
}
if repo_config.auto_assign() {
// add reviewers
println!("Adding reviewers...");
let mut author_set: HashSet<String> = HashSet::new();
author_set.insert(prhunk.author().to_string());
for blame in prhunk.blamevec() {
if author_set.contains(blame.author()) {
let blame_author_opt = author_from_commit(blame.commit(),
hunkmap.repo_name(), hunkmap.repo_owner()).await;
if blame_author_opt.is_none() {
eprintln!("[process_coverage] Unable to get blame author from bb for commit: {}", &blame.commit());
continue;
}
author_set.insert(blame.author().to_string());
let author_id = blame.author();
add_reviewers(blame.author(), review, &access_token).await;
let blame_author = blame_author_opt.expect("Empty blame_author_opt");
let author_uuid = blame_author.uuid();
if author_set.contains(author_uuid) {
continue;
}
add_reviewers(&blame_author, review, &access_token).await;
author_set.insert(author_uuid.to_string());
}
}
}
Expand Down
75 changes: 55 additions & 20 deletions vibi-dpu/src/utils/gitops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use serde::Serialize;
use sha256::digest;

use crate::bitbucket::auth::refresh_git_auth;
use crate::bitbucket::user::get_commit_bb;

use super::hunk::BlameItem;
use super::review::Review;
Expand Down Expand Up @@ -373,7 +372,7 @@ pub async fn generate_blame(review: &Review, linemap: &HashMap<String, Vec<Strin
if blamelines.len() == 0 {
continue;
}
let blamitems_opt = process_blameitem(path, commit, linenum, blamelines, review).await;
let blamitems_opt = process_blameitem(path, linenum, blamelines).await;
if blamitems_opt.is_some() {
let blameitems = blamitems_opt.expect("blameitem not found in blameitem_opt");
blamevec.extend(blameitems);
Expand All @@ -383,7 +382,7 @@ pub async fn generate_blame(review: &Review, linemap: &HashMap<String, Vec<Strin
return blamevec;
}

async fn process_blameitem(path: &str, commit: &str, linenum: &str, blamelines: Vec<&str>, review: &Review) -> Option<Vec<BlameItem>> {
async fn process_blameitem(path: &str, linenum: &str, blamelines: Vec<&str>) -> Option<Vec<BlameItem>> {
let linenumint_res = linenum.parse::<usize>();
let mut blamevec = Vec::<BlameItem>::new();
if linenumint_res.is_err() {
Expand All @@ -392,8 +391,7 @@ async fn process_blameitem(path: &str, commit: &str, linenum: &str, blamelines:
return None;
}
let linenumint = linenumint_res.expect("Uncaught error in linenumint_res");
let lineauthormap = process_blamelines(&blamelines, linenumint,
&review.repo_name(), &review.repo_owner()).await;
let lineauthormap = process_blamelines(&blamelines, linenumint).await;
let mut linebreak = linenumint;
for lidx in linenumint..(linenumint + blamelines.len()-1) {
if lineauthormap.contains_key(&lidx) && lineauthormap.contains_key(&(lidx+1)) {
Expand All @@ -408,7 +406,9 @@ async fn process_blameitem(path: &str, commit: &str, linenum: &str, blamelines:
lineitem.timestamp().to_string(),
linebreak.to_string(),
lidx.to_string(),
digest(path) ));
digest(path),
lineitem.commit().to_string())
);
linebreak = lidx + 1;
}
}
Expand All @@ -421,26 +421,61 @@ async fn process_blameitem(path: &str, commit: &str, linenum: &str, blamelines:
lineitem.timestamp().to_string(),
linebreak.to_string(),
lastidx.to_string(),
digest(path)));
digest(path),
lineitem.commit().to_string()));
}
return Some(blamevec);
}

async fn process_blamelines(blamelines: &Vec<&str>, linenum: usize,
repo_name: &str, repo_owner: &str) -> HashMap<usize, LineItem> {
async fn process_blamelines(blamelines: &Vec<&str>, linenum: usize) -> HashMap<usize, LineItem> {
let mut linemap = HashMap::<usize, LineItem>::new();
for lnum in 0..blamelines.len() {
let ln = blamelines[lnum];
let wordvec: Vec<&str> = ln.split(" ").collect();
let commit = wordvec[0];
let lineitem_opt = get_commit_bb(commit, repo_name, repo_owner).await;
if lineitem_opt.is_some() {
let lineitem = lineitem_opt.expect("Empty linemap_opt");
linemap.insert(
linenum + lnum,
lineitem
);
}
let blame_line = blamelines[lnum];
let blame_line_words: Vec<&str> = blame_line.split(" ").collect();
let commit = blame_line_words[0].to_string();
let (author, idx) = extract_author(&blame_line_words);
let timestamp = extract_timestamp(&blame_line_words, idx);
let lineitem = LineItem::new(author, timestamp, commit);
linemap.insert(
linenum + lnum,
lineitem
);
}
return linemap;
}

fn extract_author(blame_line_words: &Vec<&str>) -> (String, usize) {
let mut author = blame_line_words[1];
let mut idx = 1;
// Check if the second value is an email address (enclosed in angle brackets)
if !author.starts_with('(') && !author.ends_with('>') {
// Shift the index to the next non-empty value
while idx < blame_line_words.len() && (blame_line_words[idx] == "" || !blame_line_words[idx].starts_with('(')){
idx += 1;
}
if idx < blame_line_words.len() {
author = blame_line_words[idx];
}
} else {
// Remove the angle brackets from the email address
author = author.trim_start_matches('<').trim_end_matches('>');
}
let authorstr = author.replace("(", "")
.replace("<", "")
.replace(">", "");
return (authorstr, idx)
}

fn extract_timestamp(wordvec: &Vec<&str>, mut idx: usize) -> String {
let mut timestamp = wordvec[2];
if timestamp == "" || timestamp.starts_with('(') {
idx = idx + 1;
while idx < wordvec.len() && (wordvec[idx] == "" || wordvec[idx].starts_with('(')) {
idx = idx + 1;
}
if idx < wordvec.len() {
timestamp = wordvec[idx];
}
}
return timestamp.to_string();
}
Loading

0 comments on commit 53f3ff2

Please sign in to comment.