use axum::{
extract::{Path, State},
http::StatusCode,
Extension, Json,
};
use crate::api_v1::dto::JobStatusResponse;
use crate::state::AppState;
use gradatum_core::trust::TrustContext;
use gradatum_queue::JobStatus;
fn sanitize_job_error(raw: &str) -> &'static str {
if raw.contains("ULID invalide") || raw.contains("invalid character") {
"invalid_input"
} else if raw.contains("Vault::") || raw.contains("vault non configuré") {
"vault_error"
} else if raw.contains("Storage") || raw.contains("sqlx") || raw.contains("SQLite") {
"storage_error"
} else {
"processing_error"
}
}
pub async fn get_job(
State(state): State<AppState>,
Extension(trust): Extension<TrustContext>,
Path(id): Path<i64>,
) -> Result<Json<JobStatusResponse>, StatusCode> {
let _ = &trust;
match state.queue.get(id).await {
Ok(Some(info)) => {
if info.status == JobStatus::Pending && info.attempts > 0 {
tracing::warn!(
job_id = id,
attempts = info.attempts,
"job pending with attempts>0 — possible DB status inconsistency or unknown variant fallback"
);
}
Ok(Json(JobStatusResponse {
job_id: info.id,
status: info.status.as_str().to_string(),
attempts: info.attempts,
last_error: info
.last_error
.as_deref()
.map(|raw| sanitize_job_error(raw).to_string()),
}))
}
Ok(None) => Err(StatusCode::NOT_FOUND),
Err(e) => {
tracing::error!(error = %e, job_id = id, "queue.get failed");
Err(StatusCode::INTERNAL_SERVER_ERROR)
}
}
}
#[cfg(test)]
mod sanitize_tests {
use super::sanitize_job_error;
#[test]
fn ulid_error_maps_invalid_input() {
assert_eq!(
sanitize_job_error("ULID invalide abc123: invalid character"),
"invalid_input"
);
}
#[test]
fn vault_error_maps_vault_error() {
assert_eq!(
sanitize_job_error("Vault::open(/var/lib/gradatum/vault) failed: permission denied"),
"vault_error"
);
}
#[test]
fn storage_error_maps_storage_error() {
assert_eq!(
sanitize_job_error("Storage(\"sqlx: connection refused\")"),
"storage_error"
);
}
#[test]
fn fallback_processing_error() {
assert_eq!(
sanitize_job_error("unknown failure mode"),
"processing_error"
);
}
}