diff --git a/devprofiler/src/bitbucket/mod.rs b/devprofiler/src/bitbucket/mod.rs index 825cc62..f0db7d0 100644 --- a/devprofiler/src/bitbucket/mod.rs +++ b/devprofiler/src/bitbucket/mod.rs @@ -2,4 +2,5 @@ pub mod auth; pub mod workspace; pub mod repo; mod config; -pub mod webhook; \ No newline at end of file +pub mod webhook; +pub mod user; \ No newline at end of file diff --git a/devprofiler/src/bitbucket/user.rs b/devprofiler/src/bitbucket/user.rs new file mode 100644 index 0000000..6a9d8f1 --- /dev/null +++ b/devprofiler/src/bitbucket/user.rs @@ -0,0 +1,32 @@ +use crate::db::auth::auth_info; +use crate::db::user::save_user_to_db; +use crate::utils::auth::AuthInfo; +use crate::utils::user::{User, Provider, ProviderEnum}; +use super::config::{bitbucket_base_url, get_api}; + +pub async fn get_and_save_workspace_users(workspace_id: &str, access_token: &str) { + let base_url = bitbucket_base_url(); + let members_url = format!("{}/workspaces/{}/members", &base_url, workspace_id); + let response_json = get_api(&members_url, access_token, None).await; + for user_json in response_json { + let provider_id = user_json["user"]["uuid"].to_string().replace('"', ""); + let user = User::new( + Provider::new( + provider_id, + ProviderEnum::Bitbucket), + user_json["user"]["display_name"].to_string().replace('"', ""), + user_json["workspace"]["slug"].to_string().replace('"', ""), + None); + save_user_to_db(&user); + } +} + +pub async fn get_commit_bb(commit: &str, repo_name: &str, repo_owner: &str) { + let base_url = bitbucket_base_url(); + let commits_url = format!("{}/repositories/{repo_owner}/{repo_name}/commit/{commit}", &base_url); + println!("commits url = {}", &commits_url); + let authinfo: AuthInfo = auth_info(); + let access_token = authinfo.access_token(); + let response_json = get_api(&commits_url, access_token, None).await; + println!("response json for commits url = {:?}", &response_json); +} \ No newline at end of file diff --git a/devprofiler/src/core/review.rs b/devprofiler/src/core/review.rs index bcc4003..cc39168 100644 --- a/devprofiler/src/core/review.rs +++ b/devprofiler/src/core/review.rs @@ -2,61 +2,55 @@ use std::env; use serde_json::Value; -use crate::{utils::{hunk::{HunkMap, PrHunkItem}, review::Review, gitops::{commit_exists, git_pull, get_excluded_files, generate_diff, process_diff, generate_blame}}, db::{hunk::{get_hunk_from_db, store_hunkmap_to_db}, repo::get_clone_url_clone_dir, review::save_review_to_db}, core::coverage::process_coverage}; +use crate::{utils::{hunk::{HunkMap, PrHunkItem}, review::Review, gitops::{commit_exists, git_pull, get_excluded_files, generate_diff, process_diff, generate_blame}}, db::{hunk::{get_hunk_from_db, store_hunkmap_to_db}, repo::get_clone_url_clone_dir, review::{save_review_to_db, self}}, core::coverage::process_coverage}; pub async fn process_review(message_data: &Vec) { let review_opt = get_tasks(message_data); - match review_opt { - Some(review) => { - let hunk = get_hunk_from_db(&review); - match hunk { - Some(hunkval) => { - publish_hunkmap(&hunkval); - return;}, - None => {} - } - let mut prvec = Vec::::new(); - println!("Processing PR : {}", review.id()); - if !commit_exists(&review.base_head_commit()) || !commit_exists(&review.pr_head_commit()) { - println!("Pulling repository {} for commit history", review.repo_name()); - git_pull(&review).await; - } - let fileopt = get_excluded_files(&review); - println!("fileopt = {:?}", &fileopt); - match fileopt { - Some((_, smallfiles)) => { - let diffmap = generate_diff(&review, &smallfiles); - println!("diffmap = {:?}", &diffmap); - let diffres = process_diff(&diffmap); - match diffres { - Ok(linemap) => { - let blamevec = generate_blame(&review, &linemap); - let hmapitem = PrHunkItem::new( - review.id().to_string(), - review.author().to_string(), - blamevec, - ); - prvec.push(hmapitem); - } - Err(e) => { - eprint!("Unable to process diff : {e}"); - } - } - let hunkmap = HunkMap::new(review.provider().to_string(), - review.repo_owner().to_string(), - review.repo_name().to_string(), - prvec, - format!("{}/hunkmap", review.db_key()), - ); - store_hunkmap_to_db(&hunkmap, &review); - publish_hunkmap(&hunkmap); - process_coverage(&hunkmap).await; - }, - None => {eprintln!("No files to review for PR {}", review.id());} - }; - }, - None => { eprintln!("No review tasks found!" ); } - }; + if review_opt.is_none() { + eprintln!("No review tasks found!"); + return; + } + let review = review_opt.expect("review_opt is empty"); + let hunk = get_hunk_from_db(&review); + if hunk.is_some() { + let hunkval = hunk.expect("hunk is empty"); + publish_hunkmap(&hunkval); + eprintln!("Hunk already in db!"); + return; + } + let mut prvec = Vec::::new(); + println!("Processing PR : {}", review.id()); + if !commit_exists(&review.base_head_commit()) || !commit_exists(&review.pr_head_commit()) { + println!("Pulling repository {} for commit history", review.repo_name()); + git_pull(&review).await; + } + let fileopt = get_excluded_files(&review); + println!("fileopt = {:?}", &fileopt); + if fileopt.is_none() { + eprintln!("No files to review for PR {}", review.id()); + return; + } + let (_, smallfiles) = fileopt.expect("fileopt is empty"); + let diffmap = generate_diff(&review, &smallfiles); + println!("diffmap = {:?}", &diffmap); + let linemap = process_diff(&diffmap); + let blamevec = generate_blame(&review, &linemap).await; + let hmapitem = PrHunkItem::new( + review.id().to_string(), + review.author().to_string(), + blamevec, + ); + prvec.push(hmapitem); + let hunkmap = HunkMap::new(review.provider().to_string(), + review.repo_owner().to_string(), + review.repo_name().to_string(), + prvec, + format!("{}/hunkmap", review.db_key()), + ); + store_hunkmap_to_db(&hunkmap, &review); + publish_hunkmap(&hunkmap); + let hunkmap_async = hunkmap.clone(); + process_coverage(&hunkmap_async).await; } fn get_tasks(message_data: &Vec) -> Option{ diff --git a/devprofiler/src/core/setup.rs b/devprofiler/src/core/setup.rs index 996968d..9521235 100644 --- a/devprofiler/src/core/setup.rs +++ b/devprofiler/src/core/setup.rs @@ -9,6 +9,7 @@ use crate::bitbucket::auth::get_access_token_from_bitbucket; use crate::bitbucket::repo::get_workspace_repos; use crate::bitbucket::workspace::get_bitbucket_workspaces; use crate::bitbucket::webhook::{get_webhooks_in_repo, add_webhook}; +use crate::bitbucket::user::get_and_save_workspace_users; use crate::db::repo::save_repo_to_db; use crate::db::webhook::save_webhook_to_db; use crate::utils::repo::Repository; diff --git a/devprofiler/src/db/mod.rs b/devprofiler/src/db/mod.rs index 5e7844c..312434a 100644 --- a/devprofiler/src/db/mod.rs +++ b/devprofiler/src/db/mod.rs @@ -4,4 +4,5 @@ pub mod repo; mod config; pub mod webhook; pub mod hunk; -pub mod review; \ No newline at end of file +pub mod review; +pub mod user; \ No newline at end of file diff --git a/devprofiler/src/db/user.rs b/devprofiler/src/db/user.rs new file mode 100644 index 0000000..4c8cf04 --- /dev/null +++ b/devprofiler/src/db/user.rs @@ -0,0 +1,18 @@ +use sled::IVec; + +use crate::db::config::get_db; +use crate::utils::user::User; + +pub fn save_user_to_db(user: &User) { + let db = get_db(); + let provider_obj = user.provider(); + let user_key = format!("{}/{}/{}", + provider_obj.provider_type().to_string(), user.workspace(), provider_obj.id()); + println!("user_key = {}", &user_key); + + // Serialize repo struct to JSON + let json = serde_json::to_vec(user).expect("Failed to serialize user"); + + // Insert JSON into sled DB + db.insert(IVec::from(user_key.as_bytes()), json).expect("Failed to upsert user into sled DB"); +} diff --git a/devprofiler/src/utils/gitops.rs b/devprofiler/src/utils/gitops.rs index a445f20..c57c2e2 100644 --- a/devprofiler/src/utils/gitops.rs +++ b/devprofiler/src/utils/gitops.rs @@ -7,6 +7,7 @@ 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; @@ -181,7 +182,7 @@ pub fn generate_diff(review: &Review, smallfiles: &Vec) -> HashMap) -> Result>,Box> { +pub fn process_diff(diffmap: &HashMap) -> HashMap> { let mut linemap: HashMap> = HashMap::new(); for (filepath, diff) in diffmap { let mut limiterpos = Vec::new(); @@ -228,10 +229,10 @@ pub fn process_diff(diffmap: &HashMap) -> Result>) -> Vec{ +pub async fn generate_blame(review: &Review, linemap: &HashMap>) -> Vec{ let mut blamevec = Vec::::new(); let commit = review.pr_head_commit(); let clone_dir = review.clone_dir(); @@ -261,7 +262,8 @@ pub fn generate_blame(review: &Review, linemap: &HashMap>) - continue; } let linenumint = linenum.parse::().expect("Unable to parse linenum"); - let lineauthormap = process_blamelines(&blamelines, linenumint); + let lineauthormap = process_blamelines(&blamelines, linenumint, + &review.repo_name(), &review.repo_owner()).await; let mut linebreak = linenumint; for lidx in linenumint..(linenumint + blamelines.len()-1) { if lineauthormap.contains_key(&lidx) && lineauthormap.contains_key(&(lidx+1)) { @@ -321,11 +323,14 @@ impl LineItem { } } -fn process_blamelines(blamelines: &Vec<&str>, linenum: usize) -> HashMap { +async fn process_blamelines(blamelines: &Vec<&str>, linenum: usize, + repo_name: &str, repo_owner: &str) -> HashMap { let mut linemap = HashMap::::new(); for lnum in 0..blamelines.len() { let ln = blamelines[lnum]; let wordvec: Vec<&str> = ln.split(" ").collect(); + let commit = wordvec[0]; + let lineitem = get_commit_bb(commit, repo_name, repo_owner).await; let mut author = wordvec[1]; let mut timestamp = wordvec[2]; let mut idx = 1; diff --git a/devprofiler/src/utils/hunk.rs b/devprofiler/src/utils/hunk.rs index 97250d9..884833d 100644 --- a/devprofiler/src/utils/hunk.rs +++ b/devprofiler/src/utils/hunk.rs @@ -1,4 +1,4 @@ -#[derive(Debug, Serialize, Default, Deserialize)] +#[derive(Debug, Serialize, Default, Deserialize, Clone)] pub struct HunkMap { repo_provider: String, repo_owner: String, @@ -11,7 +11,7 @@ pub struct HunkMap { use serde::Deserialize; use serde::Serialize; -#[derive(Debug, Serialize, Default, Deserialize)] +#[derive(Debug, Serialize, Default, Deserialize, Clone)] pub struct PrHunkItem { pr_number: String, author: String, @@ -19,7 +19,7 @@ pub struct PrHunkItem { } -#[derive(Debug, Serialize, Default, Deserialize)] +#[derive(Debug, Serialize, Default, Deserialize, Clone)] pub struct BlameItem { author: String, timestamp: String, diff --git a/devprofiler/src/utils/mod.rs b/devprofiler/src/utils/mod.rs index 6038877..3532717 100644 --- a/devprofiler/src/utils/mod.rs +++ b/devprofiler/src/utils/mod.rs @@ -4,4 +4,5 @@ pub mod auth; pub mod webhook; pub mod hunk; pub mod review; -pub mod gitops; \ No newline at end of file +pub mod gitops; +pub mod user; \ No newline at end of file diff --git a/devprofiler/src/utils/user.rs b/devprofiler/src/utils/user.rs new file mode 100644 index 0000000..e3b07eb --- /dev/null +++ b/devprofiler/src/utils/user.rs @@ -0,0 +1,90 @@ +use std::fmt; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub enum ProviderEnum { + Bitbucket, + Github, +} + +impl fmt::Display for ProviderEnum { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ProviderEnum::Bitbucket => write!(f, "bitbucket"), + ProviderEnum::Github => write!(f, "github"), + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Provider { + id: String, + provider_type: ProviderEnum, +} + +impl Provider { + // Constructor + pub fn new(id: String, provider_type: ProviderEnum) -> Self { + Self { id, provider_type } + } + + // Public getter methods + pub fn id(&self) -> &String { + &self.id + } + + pub fn provider_type(&self) -> &ProviderEnum { + &self.provider_type + } + + // Public setter methods + pub fn set_id(&mut self, id: String) { + self.id = id; + } + + pub fn set_provider_type(&mut self, provider_type: ProviderEnum) { + self.provider_type = provider_type; + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct User { + provider: Provider, + name: String, + workspace: String, + aliases: Option>, +} + +impl User { + // Constructor + pub fn new(provider: Provider, name: String, workspace: String, aliases: Option>) -> Self { + Self { + provider, + name, + workspace, + aliases, + } + } + + // Public getter methods + pub fn provider(&self) -> &Provider { + &self.provider + } + + pub fn name(&self) -> &String { + &self.name + } + + pub fn workspace(&self) -> &String { + &self.workspace + } + + pub fn aliases(&self) -> &Option> { + &self.aliases + } + + pub fn set_aliases(&mut self, aliases: Option>) { + self.aliases = aliases; + } +}