166 lines
4.3 KiB
Rust
166 lines
4.3 KiB
Rust
use reqwest;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct RedmineIssue {
|
|
pub id: u32,
|
|
pub subject: String,
|
|
pub description: Option<String>,
|
|
pub status: Option<RedmineStatus>,
|
|
pub priority: Option<RedminePriority>,
|
|
pub assigned_to: Option<RedmineUser>,
|
|
pub author: Option<RedmineUser>,
|
|
pub project: Option<RedmineProject>,
|
|
pub created_on: Option<String>,
|
|
pub updated_on: Option<String>,
|
|
pub start_date: Option<String>,
|
|
pub due_date: Option<String>,
|
|
pub done_ratio: Option<u32>,
|
|
pub estimated_hours: Option<f32>,
|
|
pub spent_hours: Option<f32>,
|
|
pub journals: Option<Vec<RedmineJournal>>,
|
|
pub attachments: Option<Vec<RedmineAttachment>>,
|
|
pub custom_fields: Option<Vec<RedmineCustomField>>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct RedmineStatus {
|
|
pub id: u32,
|
|
pub name: String,
|
|
pub is_closed: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct RedminePriority {
|
|
pub id: u32,
|
|
pub name: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct RedmineUser {
|
|
pub id: u32,
|
|
pub name: String,
|
|
pub mail: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct RedmineProject {
|
|
pub id: u32,
|
|
pub name: String,
|
|
pub identifier: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct RedmineJournal {
|
|
pub id: u32,
|
|
pub user: Option<RedmineUser>,
|
|
pub notes: Option<String>,
|
|
pub created_on: Option<String>,
|
|
pub details: Option<Vec<RedmineJournalDetail>>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct RedmineJournalDetail {
|
|
pub property: Option<String>,
|
|
pub name: Option<String>,
|
|
pub old_value: Option<String>,
|
|
pub new_value: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct RedmineAttachment {
|
|
pub id: u32,
|
|
pub filename: String,
|
|
pub filesize: Option<u32>,
|
|
pub content_type: Option<String>,
|
|
pub content_url: Option<String>,
|
|
pub digest: Option<String>,
|
|
pub downloads: Option<u32>,
|
|
pub author: Option<RedmineUser>,
|
|
pub created_on: Option<String>,
|
|
pub description: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
pub struct RedmineCustomField {
|
|
pub id: u32,
|
|
pub name: String,
|
|
pub value: Option<serde_json::Value>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
struct RedmineIssueResponse {
|
|
issue: RedmineIssue,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
struct RedmineIssuesResponse {
|
|
issues: Vec<RedmineIssue>,
|
|
}
|
|
|
|
pub async fn fetch_user_issues(
|
|
api_key: &str,
|
|
url: &str,
|
|
user_id: u32,
|
|
) -> Result<Vec<RedmineIssue>, Box<dyn std::error::Error + Send + Sync>> {
|
|
let client = reqwest::Client::new();
|
|
|
|
let response = client
|
|
.get(format!("{}/issues.json", url.trim_end_matches('/')))
|
|
.query(&[
|
|
("assigned_to_id", user_id.to_string()),
|
|
("key", api_key.to_string()),
|
|
("include", "attachments,journals,relations".to_string()),
|
|
("limit", "100".to_string()),
|
|
])
|
|
.send()
|
|
.await?;
|
|
|
|
if !response.status().is_success() {
|
|
return Err(format!("Redmine API error: {}", response.status()).into());
|
|
}
|
|
|
|
let body: RedmineIssuesResponse = response.json().await?;
|
|
Ok(body.issues)
|
|
}
|
|
|
|
pub async fn fetch_issue_full(
|
|
api_key: &str,
|
|
url: &str,
|
|
issue_id: u32,
|
|
) -> Result<RedmineIssue, Box<dyn std::error::Error + Send + Sync>> {
|
|
let client = reqwest::Client::new();
|
|
|
|
let response = client
|
|
.get(format!(
|
|
"{}/issues/{}.json",
|
|
url.trim_end_matches('/'),
|
|
issue_id
|
|
))
|
|
.query(&[
|
|
("key", api_key.to_string()),
|
|
(
|
|
"include",
|
|
"attachments,journals,relations,watchers".to_string(),
|
|
),
|
|
])
|
|
.send()
|
|
.await?;
|
|
|
|
if !response.status().is_success() {
|
|
return Err(format!("Redmine API error: {}", response.status()).into());
|
|
}
|
|
|
|
let body: RedmineIssueResponse = response.json().await?;
|
|
Ok(body.issue)
|
|
}
|
|
|
|
pub async fn test_connection(
|
|
api_key: &str,
|
|
url: &str,
|
|
user_id: u32,
|
|
) -> Result<usize, Box<dyn std::error::Error + Send + Sync>> {
|
|
let issues = fetch_user_issues(api_key, url, user_id).await?;
|
|
Ok(issues.len())
|
|
}
|