Skip to main content

soma_palette/
error.rs

1//! Product error mapping for Palette UI responses.
2//!
3//! The Palette frontend renders `code`/`message`/`remediation` directly, so
4//! the JSON body is `ApplicationError` itself (already `Serialize`); this
5//! module only owns the HTTP status mapping. It delegates to
6//! `soma-http-api`'s shared classification rather than duplicating
7//! `soma-api`'s table — `product-surface` packages must not depend on one
8//! another (see `xtask check-architecture`), so both surfaces depend on the
9//! same `shared/*` crate instead.
10
11use axum::{
12    http::StatusCode,
13    response::{IntoResponse, Response},
14    Json,
15};
16use soma_application::ApplicationError;
17
18pub fn palette_error_response(error: ApplicationError) -> Response {
19    let status = palette_error_status(&error);
20    tracing::warn!(code = %error.code, "palette request failed");
21    (status, Json(error)).into_response()
22}
23
24pub fn palette_error_status(error: &ApplicationError) -> StatusCode {
25    soma_http_api::response::application_error_status(&error.code)
26}
27
28/// `404` body for a launcher id that doesn't resolve to any palette-exposed
29/// tool. The lookup happens in this crate, before any call reaches
30/// `SomaApplication`, so there is no real `ApplicationError` to map — but
31/// the body is still built as an `ApplicationError` value (rather than a
32/// hand-rolled `json!` literal) so every `/v1/palette/*` error response,
33/// found-then-failed or never-found, shares one wire shape
34/// (`code`/`message`/`retryable`/`remediation`/`details`) for the frontend.
35pub fn launcher_not_found(id: &str) -> Response {
36    let error = ApplicationError::new(
37        "launcher_not_found",
38        format!("no palette-exposed launcher entry `{id}`"),
39        false,
40        "Refresh the catalog and use a known launcher id.",
41    );
42    (StatusCode::NOT_FOUND, Json(error)).into_response()
43}
44
45#[cfg(test)]
46#[path = "error_tests.rs"]
47mod tests;