use axum::{
Json,
extract::{Path, State},
http::HeaderMap,
};
use fusillade::{ResponseStepStore, Storage};
use onwards::StoreError;
use sqlx_pool_router::PoolProvider;
use crate::AppState;
use crate::errors::{Error, Result};
#[tracing::instrument(skip_all)]
pub async fn get_response<P: PoolProvider>(
State(state): State<AppState<P>>,
headers: HeaderMap,
Path(response_id): Path<String>,
) -> Result<Json<serde_json::Value>> {
let api_key = headers
.get("authorization")
.and_then(|v| v.to_str().ok())
.and_then(|s| s.strip_prefix("Bearer "))
.ok_or_else(|| Error::Unauthenticated { message: None })?;
let owner_id: String = sqlx::query_scalar("SELECT user_id::text FROM public.api_keys WHERE secret = $1 AND is_deleted = false LIMIT 1")
.bind(api_key)
.fetch_optional(state.db.read())
.await
.map_err(|e| Error::Database(e.into()))?
.ok_or_else(|| Error::Unauthenticated { message: None })?;
let uuid_str = response_id.strip_prefix("resp_").unwrap_or(&response_id);
let head_step_uuid = uuid::Uuid::parse_str(uuid_str).map_err(|_| Error::NotFound {
resource: "response".to_string(),
id: response_id.clone(),
})?;
let auth_request_id = match state.response_step_manager.as_ref() {
Some(step_manager) => match step_manager.get_step(fusillade::StepId(head_step_uuid)).await {
Ok(Some(head_step)) => head_step.request_id.unwrap_or(fusillade::RequestId(head_step_uuid)),
Ok(None) => fusillade::RequestId(head_step_uuid),
Err(e) => return Err(Error::Database(crate::db::errors::DbError::Other(anyhow::anyhow!("{e}")))),
},
None => fusillade::RequestId(head_step_uuid),
};
let detail = state
.request_manager
.get_request_detail(auth_request_id)
.await
.map_err(|e| match e {
fusillade::FusilladeError::RequestNotFound(_) => Error::NotFound {
resource: "response".to_string(),
id: response_id.clone(),
},
_ => Error::Database(crate::db::errors::DbError::Other(anyhow::anyhow!("{e}"))),
})?;
let batch_owner = detail.batch_created_by.as_deref().unwrap_or("");
if batch_owner != owner_id {
return Err(Error::NotFound {
resource: "response".to_string(),
id: response_id,
});
}
let resp = state
.response_store
.get_response(&response_id)
.await
.map_err(|e| match e {
StoreError::NotFound(_) => Error::NotFound {
resource: "response".to_string(),
id: response_id.clone(),
},
_ => Error::Database(crate::db::errors::DbError::Other(anyhow::anyhow!("{e}"))),
})?
.ok_or_else(|| Error::NotFound {
resource: "response".to_string(),
id: response_id,
})?;
Ok(Json(resp))
}