orchai/src-tauri/src/commands/poller.rs

182 lines
5.5 KiB
Rust
Raw Normal View History

use crate::error::AppError;
use crate::models::credential::TuleapCredentials;
use crate::models::module::{ProjectModule, MODULE_TULEAP_AUTO_RESOLVE};
use crate::models::ticket::{ProcessedTicket, ProjectThroughputStats};
use crate::models::tracker::WatchedTracker;
use crate::services::tuleap_client::TuleapClient;
use crate::services::{crypto, filter_engine, notifier};
use crate::AppState;
use tauri::{Emitter, State};
#[tauri::command]
pub async fn manual_poll(
state: State<'_, AppState>,
app_handle: tauri::AppHandle,
tracker_id: String,
) -> Result<Vec<ProcessedTicket>, AppError> {
let (tracker, client) = {
let db = state
.db
.lock()
.map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;
let tracker = WatchedTracker::get_by_id(&db, &tracker_id)?;
let module_enabled =
ProjectModule::is_enabled(&db, &tracker.project_id, MODULE_TULEAP_AUTO_RESOLVE)?;
if !module_enabled {
return Err(AppError::from(
"Le module Polling Tuleap + auto-resolve est désactivé pour ce projet".to_string(),
));
}
if tracker.status != "valid" {
return Err(AppError::from(
"Tracker is invalid. Reconfigure analyst/developer agents first.".to_string(),
));
}
let cred = TuleapCredentials::get_for_project(&db, &tracker.project_id)?
.ok_or_else(|| AppError::from("No Tuleap credentials configured".to_string()))?;
let password = crypto::decrypt(&state.encryption_key, &cred.password_encrypted)
.map_err(AppError::from)?;
let client = TuleapClient::new(
&state.http_client,
&cred.tuleap_url,
&cred.username,
&password,
);
(tracker, client)
}; // lock dropped here
let _ = app_handle.emit(
"polling-started",
serde_json::json!({
"project_id": &tracker.project_id,
"tracker_id": &tracker.id,
"tracker_label": &tracker.tracker_label,
"source": "manual",
}),
);
let artifacts = match client.get_artifacts(tracker.tracker_id).await {
Ok(a) => a,
Err(e) => {
let _ = app_handle.emit(
"polling-error",
serde_json::json!({
"project_id": &tracker.project_id,
"tracker_id": &tracker.id,
"tracker_label": &tracker.tracker_label,
"source": "manual",
"error": e,
}),
);
return Err(AppError::from(e));
}
};
let filtered = filter_engine::apply_filters(&artifacts, &tracker.filters);
let mut newly_inserted: Vec<ProcessedTicket> = Vec::new();
{
let db = state
.db
.lock()
.map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;
for artifact in &filtered {
let artifact_id = artifact.get("id").and_then(|v| v.as_i64()).unwrap_or(0) as i32;
let artifact_title = artifact
.get("title")
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string();
let artifact_data =
serde_json::to_string(artifact).unwrap_or_else(|_| "{}".to_string());
if let Some(ticket) = ProcessedTicket::insert_if_new(
&db,
&tracker.project_id,
&tracker.id,
artifact_id,
&artifact_title,
&artifact_data,
)? {
newly_inserted.push(ticket);
}
}
WatchedTracker::update_last_polled(&db, &tracker.id)?;
}
if !newly_inserted.is_empty() {
let _ = app_handle.emit(
"new-tickets-detected",
serde_json::json!({
"project_id": &tracker.project_id,
"tracker_id": &tracker.id,
"tracker_label": &tracker.tracker_label,
"count": newly_inserted.len(),
}),
);
for ticket in &newly_inserted {
notifier::notify_new_ticket(
&state.db,
&app_handle,
&tracker.project_id,
&ticket.id,
ticket.artifact_id,
&ticket.artifact_title,
);
}
}
let _ = app_handle.emit(
"polling-finished",
serde_json::json!({
"project_id": &tracker.project_id,
"tracker_id": &tracker.id,
"tracker_label": &tracker.tracker_label,
"source": "manual",
"new_tickets_count": newly_inserted.len(),
}),
);
Ok(newly_inserted)
}
#[tauri::command]
pub fn get_queue_status(
state: State<'_, AppState>,
project_id: String,
) -> Result<Vec<ProcessedTicket>, AppError> {
let db = state
.db
.lock()
.map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;
let tickets = ProcessedTicket::list_by_project(&db, &project_id)?;
Ok(tickets)
}
#[tauri::command]
pub fn get_project_throughput(
state: State<'_, AppState>,
project_id: String,
) -> Result<ProjectThroughputStats, AppError> {
let db = state
.db
.lock()
.map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;
let stats = ProcessedTicket::get_project_throughput_stats(&db, &project_id)?;
Ok(stats)
}