use std::collections::HashSet; use std::fs::OpenOptions; use std::io::prelude::*; use std::io::BufReader; use std::path::Path; use reqwest::{Client, StatusCode}; use serde_json::{json, Value}; use tokio; #[tokio::main] async fn main() -> Result<(), Box> { let jira_base_url = "https://jira.someserver.com"; let username = "userName"; let password = "passWord! @#"; let project_key = "projectKey"; let client = Client::new(); // 1. Pull a project's component list let url = format!("{}/rest/api/2/project/{}/components", jira_base_url, project_key); let raw_data = client .get(&url) .basic_auth(username, Some(password)) .send() .await? .json::() .await?; // 2. Create a mapping of component ID, component name, and the assignee name let component_data: Vec = raw_data .as_array() .unwrap() .iter() .map(|component| { json!({ "id": component["id"], "name": component["name"], "assignee": component["assignee"]["name"] }) }) .collect(); // 3. Show a comma-separated list of the usernames and ask which username we want to replace let mut usernames: HashSet = HashSet::new(); for component in &component_data { usernames.insert(component["assignee"].as_str().unwrap().to_string()); } println!("Usernames: {}", usernames.iter().cloned().collect::>().join(", ")); let mut old_username = String::new(); println!("Enter the username you want to replace: "); std::io::stdin().read_line(&mut old_username)?; let old_username = old_username.trim(); // 4. Check if the entered username is in the list if !usernames.contains(old_username) { println!("Username not found in the list. Please select a name from the list."); return Ok(()); } // 5. Ask for the new username let mut new_username = String::new(); println!("Enter the new username: "); std::io::stdin().read_line(&mut new_username)?; let new_username = new_username.trim(); // Create a folder for the source username and history.txt file let history_folder = Path::new(old_username); std::fs::create_dir_all(&history_folder)?; let history_file_path = history_folder.join("history.txt"); let mut history_file = OpenOptions::new() .write(true) .append(true) .create(true) .open(&history_file_path)?; // 6. Create a new mapping of the IDs and new assignees we will be changing let components_to_update: Vec<&Value> = component_data .iter() .filter(|component| component["assignee"] == old_username) .collect(); // 7. Push an update to the Jira Server API to update those component leads for component in components_to_update { let component_id = component["id"].as_str().unwrap(); let component_name = component["name"].as_str().unwrap(); let url = format!("{}/rest/api/2/component/{}", jira_base_url, component_id); let post_data = json!({ "leadUserName": new_username }); let res = client .put(&url) .basic_auth(username, Some(password)) .json(&post_data) .send() .await?; if res.status() == StatusCode::OK { // Save the changed component ID and timestamp to the history file let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); let history_entry = format!( "Updated component {} ({}) with new assignee {} at {}\n", component_name, component_id, new_username, timestamp ); history_file.write_all(history_entry.as_bytes())?; } else { eprintln!( "Failed to update component {} ({}), status code: {}", component_name, component_id, res.status() ); } } Ok(()) }