From 7a4a98820868288bd02e5255756da9529ff65938 Mon Sep 17 00:00:00 2001 From: Hare Date: Fri, 18 Sep 2026 18:56:06 +0900 Subject: [PATCH] Document media type and deletion conflicts --- crates/server-api/src/lib.rs | 4 +- crates/workspace-server/src/server.rs | 53 +++++++++++++++++++++++++-- openapi/server-api.json | 12 +++++- 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/crates/server-api/src/lib.rs b/crates/server-api/src/lib.rs index cb4bc6515..bc1e943a6 100644 --- a/crates/server-api/src/lib.rs +++ b/crates/server-api/src/lib.rs @@ -159,7 +159,7 @@ pub trait ServerApi { status = 201, alternate_status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 409, 413, 422, 500], + additional_error_statuses = [401, 403, 404, 409, 413, 415, 422, 500], bearer_auth = true, browser_auth = true, normalize_body_errors = true @@ -5236,7 +5236,7 @@ mod openapi_artifact_tests { serde_json::json!([]) ); for status in [ - "200", "201", "400", "401", "403", "404", "409", "413", "422", "500", + "200", "201", "400", "401", "403", "404", "409", "413", "415", "422", "500", ] { assert!( repository_create["responses"][status].is_object(), diff --git a/crates/workspace-server/src/server.rs b/crates/workspace-server/src/server.rs index 74fb99708..9ca63cad4 100644 --- a/crates/workspace-server/src/server.rs +++ b/crates/workspace-server/src/server.rs @@ -1688,11 +1688,11 @@ async fn authorize_scoped_workspace_request( .map_err(server_error_response)? .is_some_and(|workspace| workspace.state == "active") { - return Err(( + return Err(repository_api_rejection( + &request_path, StatusCode::CONFLICT, "Workspace is deleting and no longer accepts mutations", - ) - .into_response()); + )); } return Ok(()); } @@ -19113,6 +19113,24 @@ mod tests { } } + #[tokio::test] + async fn repository_conflict_rejections_use_the_typed_error_contract() { + let response = repository_api_rejection( + "/api/w/workspace-test/repositories", + StatusCode::CONFLICT, + "Workspace is deleting and no longer accepts mutations", + ); + assert_eq!(response.status(), StatusCode::CONFLICT); + let body: server_api::RepositoryApiError = + serde_json::from_slice(&to_bytes(response.into_body(), usize::MAX).await.unwrap()) + .unwrap(); + assert_eq!(body.error, "Conflict"); + assert_eq!( + body.message, + "Workspace is deleting and no longer accepts mutations" + ); + } + #[test] fn backend_resource_timeout_maps_to_gateway_timeout() { assert_eq!( @@ -22968,6 +22986,35 @@ mod tests { .is_ok() ); + let unsupported_media_type = app + .clone() + .oneshot( + Request::builder() + .method(Method::POST) + .uri(&repositories_uri) + .header( + axum::http::header::COOKIE, + "yoi_workspace_session=browser-session-auth", + ) + .header(ORIGIN, &expected_origin) + .body(Body::from("{}")) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!( + unsupported_media_type.status(), + StatusCode::UNSUPPORTED_MEDIA_TYPE + ); + assert!( + serde_json::from_slice::( + &to_bytes(unsupported_media_type.into_body(), usize::MAX) + .await + .unwrap() + ) + .is_ok() + ); + let oversized = app .clone() .oneshot( diff --git a/openapi/server-api.json b/openapi/server-api.json index 3d7ee96dc..4e45d76b0 100644 --- a/openapi/server-api.json +++ b/openapi/server-api.json @@ -353,7 +353,7 @@ "info": { "title": "Yoi Server API", "version": "0.1.0", - "x-yoi-source-digest": "sha256:37e4ff70a1e2a6fb75a514b03a6d14306ca7fc96ef53a3271186f29acc558e00" + "x-yoi-source-digest": "sha256:bac8ba9340cf541183e96715dd4ccd8367f965cdcfc398e6d5091f26a903e1ef" }, "openapi": "3.1.0", "paths": { @@ -710,6 +710,16 @@ }, "description": "Error response" }, + "415": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryApiError" + } + } + }, + "description": "Error response" + }, "422": { "content": { "application/json": {