dashboard: return problem details directly from client-error handlers
This commit is contained in:
parent
2b0c51badf
commit
65ad994c85
8 changed files with 102 additions and 100 deletions
|
|
@ -114,17 +114,19 @@ pub(super) async fn post_tool_groups(
|
|||
State(state): State<AppState>,
|
||||
AxumPath(name): AxumPath<String>,
|
||||
axum::Json(body): axum::Json<SetToolGroupsBody>,
|
||||
) -> Response {
|
||||
) -> Result<Response, ProblemDetails> {
|
||||
let logical = strip_container_prefix(&name);
|
||||
// `guard_agent_name` yields a ready-made rejection `Response`; pass it
|
||||
// through as `Ok` (axum sends it verbatim) rather than re-deriving a
|
||||
// `ProblemDetails` — the guard is shared with `-> Response` handlers.
|
||||
if let Some(reject) = guard_agent_name(&state, &logical).await {
|
||||
return reject;
|
||||
return Ok(reject);
|
||||
}
|
||||
// Validate group names before queuing — fail fast so the operator
|
||||
// sees the error immediately rather than waiting for the worker.
|
||||
if let Err(e) = crate::tool_groups::validate_groups(&body.groups) {
|
||||
return ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||
.with_detail(format!("invalid tool-groups for {logical}: {e}"))
|
||||
.into_response();
|
||||
return Err(ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||
.with_detail(format!("invalid tool-groups for {logical}: {e}")));
|
||||
}
|
||||
// Enqueue a PermChange so the JSON file write is serialised through
|
||||
// the FIFO worker. Prevents concurrent batch-apply actions for
|
||||
|
|
@ -139,7 +141,7 @@ pub(super) async fn post_tool_groups(
|
|||
);
|
||||
state.coord.emit_rebuild_queue_snapshot();
|
||||
tracing::info!(agent = %logical, groups = ?body.groups, "operator: set tool-groups via dashboard");
|
||||
(StatusCode::OK, "ok").into_response()
|
||||
Ok((StatusCode::OK, "ok").into_response())
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
|
@ -197,10 +199,10 @@ pub(super) async fn post_capabilities(
|
|||
State(state): State<AppState>,
|
||||
AxumPath(name): AxumPath<String>,
|
||||
axum::Json(body): axum::Json<SetCapabilitiesBody>,
|
||||
) -> Response {
|
||||
) -> Result<Response, ProblemDetails> {
|
||||
let logical = strip_container_prefix(&name);
|
||||
if let Some(reject) = guard_agent_name(&state, &logical).await {
|
||||
return reject;
|
||||
return Ok(reject);
|
||||
}
|
||||
let known: Vec<&str> = hive_sh4re::Capability::ALL
|
||||
.iter()
|
||||
|
|
@ -208,9 +210,8 @@ pub(super) async fn post_capabilities(
|
|||
.collect();
|
||||
for cap in &body.caps {
|
||||
if !known.contains(&cap.as_str()) {
|
||||
return ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||
.with_detail(format!("unknown capability: {cap}"))
|
||||
.into_response();
|
||||
return Err(ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||
.with_detail(format!("unknown capability: {cap}")));
|
||||
}
|
||||
}
|
||||
// Enqueue a PermChange so the JSON file write is serialised through
|
||||
|
|
@ -226,7 +227,7 @@ pub(super) async fn post_capabilities(
|
|||
);
|
||||
state.coord.emit_rebuild_queue_snapshot();
|
||||
tracing::info!(agent = %logical, caps = ?body.caps, "operator: set capabilities via dashboard");
|
||||
(StatusCode::OK, "ok").into_response()
|
||||
Ok((StatusCode::OK, "ok").into_response())
|
||||
}
|
||||
|
||||
/// One agent's slice of a batch permission change. Sparse: an omitted
|
||||
|
|
@ -261,7 +262,7 @@ type StagedPerm = (String, Option<Vec<String>>, Option<Vec<String>>);
|
|||
pub(super) async fn post_permissions(
|
||||
State(state): State<AppState>,
|
||||
axum::Json(body): axum::Json<BatchPermsBody>,
|
||||
) -> Response {
|
||||
) -> Result<Response, ProblemDetails> {
|
||||
let known_caps: Vec<&str> = hive_sh4re::Capability::ALL
|
||||
.iter()
|
||||
.map(|c| c.as_str())
|
||||
|
|
@ -273,21 +274,19 @@ pub(super) async fn post_permissions(
|
|||
for change in &body.changes {
|
||||
let logical = strip_container_prefix(&change.agent);
|
||||
if let Some(reject) = guard_agent_name(&state, &logical).await {
|
||||
return reject;
|
||||
return Ok(reject);
|
||||
}
|
||||
if let Some(groups) = &change.tool_groups
|
||||
&& let Err(e) = crate::tool_groups::validate_groups(groups)
|
||||
{
|
||||
return ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||
.with_detail(format!("invalid tool-groups for {logical}: {e}"))
|
||||
.into_response();
|
||||
return Err(ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||
.with_detail(format!("invalid tool-groups for {logical}: {e}")));
|
||||
}
|
||||
if let Some(caps) = &change.capabilities {
|
||||
for cap in caps {
|
||||
if !known_caps.contains(&cap.as_str()) {
|
||||
return ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||
.with_detail(format!("unknown capability for {logical}: {cap}"))
|
||||
.into_response();
|
||||
return Err(ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||
.with_detail(format!("unknown capability for {logical}: {cap}")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -310,7 +309,7 @@ pub(super) async fn post_permissions(
|
|||
tracing::info!(agent = %logical, "operator: batch perm change via dashboard");
|
||||
}
|
||||
state.coord.emit_rebuild_queue_snapshot();
|
||||
(StatusCode::OK, "ok").into_response()
|
||||
Ok((StatusCode::OK, "ok").into_response())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
Loading…
Reference in a new issue