use anyhow::Result;
use base64::Engine;
use super::IntegrationOutput;
pub fn log_work(
jira_url: &str,
jira_email: &str,
api_token: &str,
issue_key: &str,
time_spent: &str,
started: &str,
description: &str,
) -> Result<IntegrationOutput> {
let url = format!(
"{}/rest/api/2/issue/{}/worklog",
jira_url.trim_end_matches('/'),
issue_key
);
let auth = base64::engine::general_purpose::STANDARD
.encode(format!("{}:{}", jira_email, api_token));
let mut body = serde_json::json!({
"timeSpent": time_spent,
"started": started,
});
if !description.is_empty() {
body["comment"] = serde_json::json!(description);
}
let client = reqwest::blocking::Client::new();
let response = client
.post(&url)
.header("Authorization", format!("Basic {}", auth))
.header("Content-Type", "application/json")
.json(&body)
.send();
match response {
Ok(resp) => {
let status = resp.status();
let body_text = resp.text().unwrap_or_default();
if status.is_success() {
let worklog_id = serde_json::from_str::<serde_json::Value>(&body_text)
.ok()
.and_then(|v| v["id"].as_str().map(|s| s.to_string()));
Ok(IntegrationOutput {
success: true,
messages: vec![
format!("[JIRA] Logged {} to {}", time_spent, issue_key),
format!("[JIRA] Response: {}", status),
],
worklog_id,
})
} else {
Ok(IntegrationOutput {
success: false,
messages: vec![
format!("[JIRA] Failed to log work: HTTP {}", status),
format!("[JIRA] Response: {}", body_text),
],
worklog_id: None,
})
}
}
Err(e) => Ok(IntegrationOutput {
success: false,
messages: vec![format!("[JIRA] Request failed: {}", e)],
worklog_id: None,
}),
}
}
pub fn delete_work(
jira_url: &str,
jira_email: &str,
api_token: &str,
issue_key: &str,
worklog_id: &str,
) -> Result<IntegrationOutput> {
let url = format!(
"{}/rest/api/2/issue/{}/worklog/{}",
jira_url.trim_end_matches('/'),
issue_key,
worklog_id
);
let auth = base64::engine::general_purpose::STANDARD
.encode(format!("{}:{}", jira_email, api_token));
let client = reqwest::blocking::Client::new();
let response = client
.delete(&url)
.header("Authorization", format!("Basic {}", auth))
.send();
match response {
Ok(resp) => {
let status = resp.status();
if status.is_success() || status.as_u16() == 404 {
Ok(IntegrationOutput {
success: true,
messages: vec![format!("[JIRA] Deleted worklog {} from {}", worklog_id, issue_key)],
worklog_id: None,
})
} else {
let body_text = resp.text().unwrap_or_default();
Ok(IntegrationOutput {
success: false,
messages: vec![
format!("[JIRA] Failed to delete worklog: HTTP {}", status),
format!("[JIRA] Response: {}", body_text),
],
worklog_id: None,
})
}
}
Err(e) => Ok(IntegrationOutput {
success: false,
messages: vec![format!("[JIRA] Request failed: {}", e)],
worklog_id: None,
}),
}
}
pub fn fetch_issue_summary(
jira_url: &str,
jira_email: &str,
api_token: &str,
issue_key: &str,
) -> Result<String> {
let url = format!(
"{}/rest/api/2/issue/{}?fields=summary",
jira_url.trim_end_matches('/'),
issue_key
);
let auth = base64::engine::general_purpose::STANDARD
.encode(format!("{}:{}", jira_email, api_token));
let client = reqwest::blocking::Client::new();
let response = client
.get(&url)
.header("Authorization", format!("Basic {}", auth))
.header("Content-Type", "application/json")
.send()?;
if !response.status().is_success() {
anyhow::bail!("HTTP {}", response.status());
}
let body: serde_json::Value = response.json()?;
let summary = body["fields"]["summary"]
.as_str()
.unwrap_or("")
.to_string();
Ok(summary)
}
pub fn open_issue(jira_url: &str, issue_key: &str) -> Result<IntegrationOutput> {
let url = format!(
"{}/browse/{}",
jira_url.trim_end_matches('/'),
issue_key
);
let command = if cfg!(target_os = "linux") {
format!("xdg-open '{}'", url)
} else if cfg!(target_os = "macos") {
format!("open '{}'", url)
} else if cfg!(target_os = "windows") {
format!("start '{}'", url)
} else {
format!("xdg-open '{}'", url)
};
let mut process = if cfg!(target_os = "windows") {
let mut cmd = std::process::Command::new("cmd");
cmd.args(&["/C", &command]);
cmd
} else {
let mut cmd = std::process::Command::new("sh");
cmd.args(&["-c", &command]);
cmd
};
process.stdin(std::process::Stdio::null());
process.stdout(std::process::Stdio::piped());
process.stderr(std::process::Stdio::piped());
match process.output() {
Ok(output) => {
if output.status.success() {
Ok(IntegrationOutput {
success: true,
messages: vec![format!("[JIRA] Opened {}", url)],
worklog_id: None,
})
} else {
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
Ok(IntegrationOutput {
success: false,
messages: vec![
format!("[JIRA] Failed to open browser: {}", output.status),
format!("[JIRA] stderr: {}", stderr),
],
worklog_id: None,
})
}
}
Err(e) => Ok(IntegrationOutput {
success: false,
messages: vec![format!("[JIRA] Failed to execute open command: {}", e)],
worklog_id: None,
}),
}
}