Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -510,6 +510,15 @@ pub(crate) async fn create_field_definition(
|
||||
}
|
||||
}
|
||||
|
||||
/// Field-level rejection detail for `set_fields`, so the UI can highlight the field.
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub(crate) struct FieldErrorView {
|
||||
/// The flexible-field key that was rejected.
|
||||
pub field: String,
|
||||
/// Machine code: "unknown" | "type_mismatch" | "unresolved".
|
||||
pub code: String,
|
||||
}
|
||||
|
||||
/// Replace an object's flexible-field values (validated against the registry).
|
||||
///
|
||||
/// **Replace semantics:** the body is the *complete* desired field set. Omitting a key
|
||||
@@ -525,7 +534,7 @@ pub(crate) async fn create_field_definition(
|
||||
(status = 401),
|
||||
(status = 403),
|
||||
(status = 404, description = "Object not found"),
|
||||
(status = 422, description = "Unknown field, type mismatch, or unresolved reference")
|
||||
(status = 422, body = FieldErrorView, description = "A field was rejected")
|
||||
)
|
||||
)]
|
||||
pub(crate) async fn set_fields(
|
||||
@@ -533,34 +542,57 @@ pub(crate) async fn set_fields(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<String>,
|
||||
Json(values): Json<serde_json::Map<String, serde_json::Value>>,
|
||||
) -> Result<StatusCode, StatusCode> {
|
||||
let object_id = id.parse::<ObjectId>().map_err(|_| StatusCode::NOT_FOUND)?;
|
||||
) -> axum::response::Response {
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
let mut tx = state
|
||||
.db
|
||||
.pool()
|
||||
.begin()
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
let Ok(object_id) = id.parse::<ObjectId>() else {
|
||||
return StatusCode::NOT_FOUND.into_response();
|
||||
};
|
||||
|
||||
let mut tx = match state.db.pool().begin().await {
|
||||
Ok(tx) => tx,
|
||||
Err(_) => return StatusCode::INTERNAL_SERVER_ERROR.into_response(),
|
||||
};
|
||||
|
||||
let result =
|
||||
db::catalog::set_object_fields(&mut tx, actor(&auth.user), object_id, &values).await;
|
||||
|
||||
match result {
|
||||
Ok(()) => {
|
||||
tx.commit()
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
if tx.commit().await.is_err() {
|
||||
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
|
||||
}
|
||||
|
||||
reindex(&state, object_id).await;
|
||||
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
StatusCode::NO_CONTENT.into_response()
|
||||
}
|
||||
Err(db::catalog::FieldError::ObjectNotFound) => Err(StatusCode::NOT_FOUND),
|
||||
Err(db::catalog::FieldError::Db(_)) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
Err(db::catalog::FieldError::UnknownField(_)) => Err(StatusCode::UNPROCESSABLE_ENTITY),
|
||||
Err(db::catalog::FieldError::TypeMismatch { .. }) => Err(StatusCode::UNPROCESSABLE_ENTITY),
|
||||
Err(db::catalog::FieldError::Unresolved { .. }) => Err(StatusCode::UNPROCESSABLE_ENTITY),
|
||||
Err(db::catalog::FieldError::ObjectNotFound) => StatusCode::NOT_FOUND.into_response(),
|
||||
Err(db::catalog::FieldError::Db(_)) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
|
||||
Err(db::catalog::FieldError::UnknownField(field)) => (
|
||||
StatusCode::UNPROCESSABLE_ENTITY,
|
||||
Json(FieldErrorView {
|
||||
field,
|
||||
code: "unknown".to_owned(),
|
||||
}),
|
||||
)
|
||||
.into_response(),
|
||||
Err(db::catalog::FieldError::TypeMismatch { field, .. }) => (
|
||||
StatusCode::UNPROCESSABLE_ENTITY,
|
||||
Json(FieldErrorView {
|
||||
field,
|
||||
code: "type_mismatch".to_owned(),
|
||||
}),
|
||||
)
|
||||
.into_response(),
|
||||
Err(db::catalog::FieldError::Unresolved { field, .. }) => (
|
||||
StatusCode::UNPROCESSABLE_ENTITY,
|
||||
Json(FieldErrorView {
|
||||
field,
|
||||
code: "unresolved".to_owned(),
|
||||
}),
|
||||
)
|
||||
.into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user