use std::sync::Arc;
use axum::{
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use serde_json::json;
use crate::{
auth::AuthContext,
request_id::RequestId,
status::SubmissionStatusRecord,
AppState,
};
pub async fn get_submission_status(
State(state): State<Arc<AppState>>,
auth: AuthContext,
Path(raw_id): Path<String>,
) -> impl IntoResponse {
let request_id: RequestId = match raw_id.parse() {
Ok(id) => id,
Err(_) => {
return (
StatusCode::NOT_FOUND,
Json(not_found_body(&raw_id)),
)
.into_response();
}
};
match state.status_store.get(&request_id, &auth.key_id) {
Ok(Some(record)) => {
(StatusCode::OK, Json(serialize_record(&record))).into_response()
}
Ok(None) => {
let body = json!({
"status": "error",
"code": "submission_not_found",
"message": "Submission status was not found or has expired.",
"request_id": &raw_id,
"target_request_id": request_id.as_str(),
});
(StatusCode::NOT_FOUND, Json(body)).into_response()
}
Err(e) => {
tracing::error!(error = %e, event = "status_store_error",
request_id = %request_id, "status store get failed");
state.metrics.status_store_error("get");
let body = json!({
"status": "error",
"code": "status_store_unavailable",
"message": "Status store is temporarily unavailable.",
"request_id": &raw_id,
});
(StatusCode::SERVICE_UNAVAILABLE, Json(body)).into_response()
}
}
}
fn serialize_record(r: &SubmissionStatusRecord) -> serde_json::Value {
json!({
"request_id": r.request_id,
"status": r.status,
"code": r.code,
"message": r.message,
"recipient_domains": r.recipient_domains,
"recipient_count": r.recipient_count,
"created_at": r.created_at.to_rfc3339(),
"updated_at": r.updated_at.to_rfc3339(),
"expires_at": r.expires_at.to_rfc3339(),
})
}
fn not_found_body(request_id: &str) -> serde_json::Value {
let lookup_id = RequestId::new();
json!({
"status": "error",
"code": "submission_not_found",
"message": "Submission status was not found or has expired.",
"request_id": request_id,
"lookup_request_id": lookup_id,
})
}