fix(api): enable typed creation error hooks on generated Backend clients
This commit is contained in:
@@ -1909,8 +1909,9 @@ pub trait ServerApi {
|
||||
"/api/workers",
|
||||
status = 200,
|
||||
error_status = 400,
|
||||
additional_error_statuses = [401, 403, 404, 409, 500, 502, 503],
|
||||
additional_error_statuses = [401, 403, 404, 409, 500, 502, 503, 504],
|
||||
additional_error_content_types = ["application/problem+json"],
|
||||
client_error_hook = true,
|
||||
bearer_auth = true,
|
||||
browser_auth = true
|
||||
)]
|
||||
@@ -1976,7 +1977,8 @@ pub trait ServerApi {
|
||||
"/api/w/{workspace_id}/worker-control/workers",
|
||||
status = 200,
|
||||
error_status = 400,
|
||||
additional_error_statuses = [401, 403, 404, 409, 500, 502, 503],
|
||||
additional_error_statuses = [401, 403, 404, 409, 500, 502, 503, 504],
|
||||
client_error_hook = true,
|
||||
openapi = false,
|
||||
)]
|
||||
async fn worker_control_spawn(
|
||||
@@ -2101,8 +2103,9 @@ pub trait ServerApi {
|
||||
"/api/w/{workspace_id}/workers",
|
||||
status = 200,
|
||||
error_status = 400,
|
||||
additional_error_statuses = [401, 403, 404, 409, 500, 502, 503],
|
||||
additional_error_statuses = [401, 403, 404, 409, 500, 502, 503, 504],
|
||||
additional_error_content_types = ["application/problem+json"],
|
||||
client_error_hook = true,
|
||||
bearer_auth = true,
|
||||
browser_auth = true
|
||||
)]
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
//! Generated Backend client → isolated loopback HTTP response → typed result.
|
||||
//! The server response producer is substituted; authorization and persistence are
|
||||
//! exercised by workspace-server's connected tests, not by this codec connection.
|
||||
use runtime_api::startup::*;
|
||||
use server_api::{CreateWorkspaceWorkerRequest, ServerApiClient, client_support, server_support};
|
||||
use server_support::framework::{Body, Response, Router, post};
|
||||
|
||||
struct ServerTask(tokio::task::JoinHandle<()>);
|
||||
impl Drop for ServerTask {
|
||||
fn drop(&mut self) {
|
||||
self.0.abort();
|
||||
}
|
||||
}
|
||||
|
||||
fn request() -> CreateWorkspaceWorkerRequest {
|
||||
serde_json::from_value(serde_json::json!({
|
||||
"runtime_id": "runtime", "display_name": "Worker", "feature_connections": {}
|
||||
}))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn every_creation_client_decodes_actual_problem_status_and_operation_facts() {
|
||||
let timeout = WorkerCreateFailure::from_cause(
|
||||
StartupCause::Boundary(StartupBoundaryCause {
|
||||
reason: StartupBoundaryReason::Timeout,
|
||||
}),
|
||||
StartupOperationFacts {
|
||||
acceptance: CreationAcceptance::Unknown,
|
||||
execution: ExecutionOutcome::Unknown,
|
||||
finalize: None,
|
||||
backend_compensation: None,
|
||||
correlation: None,
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
.problem(
|
||||
StartupBoundary::Browser,
|
||||
&SessionFailureVisibility::default(),
|
||||
"",
|
||||
)
|
||||
.unwrap();
|
||||
for (status, body) in [
|
||||
(409, include_bytes!("../../runtime-api/tests/fixtures/startup-browser-session-record-denied.problem.json").to_vec()),
|
||||
(502, include_bytes!("../../runtime-api/tests/fixtures/startup-browser-registration-response-lost.problem.json").to_vec()),
|
||||
(504, timeout.into_body()),
|
||||
] {
|
||||
let DecodeStartupProblem::Known(expected) = decode_startup_problem(&body, status, PROBLEM_CONTENT_TYPE, StartupBoundary::Browser) else {
|
||||
panic!("producer fixture must have a known typed cause");
|
||||
};
|
||||
let router = ["/api/workers", "/api/w/workspace/workers", "/api/w/workspace/worker-control/workers"]
|
||||
.into_iter().fold(Router::new(), |router, path| {
|
||||
let body = body.clone();
|
||||
router.route(path, post(move || {
|
||||
let body = body.clone();
|
||||
async move { Response::builder().status(status).header("content-type", PROBLEM_CONTENT_TYPE).body(Body::from(body)).unwrap() }
|
||||
}))
|
||||
});
|
||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let address = listener.local_addr().unwrap();
|
||||
let _server = ServerTask(tokio::spawn(async move {
|
||||
server_support::framework::serve(listener, router).await.unwrap();
|
||||
}));
|
||||
let client = ServerApiClient::builder(format!("http://{address}"))
|
||||
.unwrap().request_timeout(std::time::Duration::from_secs(3)).build().unwrap();
|
||||
for result in [
|
||||
client.workspace_worker_create("workspace".into(), request()).await,
|
||||
client.workspace_worker_create_alias(request()).await,
|
||||
client.worker_control_spawn("workspace".into(), request()).await,
|
||||
] {
|
||||
let Err(client_support::ClientError::Public { status: actual_status, error }) = result else {
|
||||
panic!("generated route must invoke the Problem decoder, not legacy Deserialize");
|
||||
};
|
||||
assert_eq!(actual_status.as_u16(), status);
|
||||
let actual = error.failure().expect("retain typed non-HTTP failure");
|
||||
assert_eq!(actual.startup().primary, expected.startup().primary);
|
||||
assert_eq!(actual.public_facts(), expected.public_facts());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creation_openapi_declares_gateway_timeout_with_both_error_representations() {
|
||||
let document: serde_json::Value = serde_json::from_str(
|
||||
&server_api::canonical_openapi_document()
|
||||
.unwrap()
|
||||
.to_json()
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
for path in ["/api/workers", "/api/w/{workspace_id}/workers"] {
|
||||
let content = &document["paths"][path]["post"]["responses"]["504"]["content"];
|
||||
assert!(content.get("application/problem+json").is_some());
|
||||
assert!(content.get("application/json").is_some());
|
||||
}
|
||||
}
|
||||
+31
-1
@@ -24199,7 +24199,7 @@
|
||||
"info": {
|
||||
"title": "Yoi Server API",
|
||||
"version": "0.1.0",
|
||||
"x-yoi-source-digest": "sha256:afde3903f29b9087390b69f9761a935cd5004c4dab17ce684f4149c4abae89c4"
|
||||
"x-yoi-source-digest": "sha256:2dad54b31293643566259ea555e11c46effce33aab9f30c8a861a46303024bad"
|
||||
},
|
||||
"openapi": "3.1.0",
|
||||
"paths": {
|
||||
@@ -49679,6 +49679,21 @@
|
||||
}
|
||||
},
|
||||
"description": "Error response"
|
||||
},
|
||||
"504": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RepositoryApiError"
|
||||
}
|
||||
},
|
||||
"application/problem+json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/StartupProblemDetails"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Error response"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
@@ -50936,6 +50951,21 @@
|
||||
}
|
||||
},
|
||||
"description": "Error response"
|
||||
},
|
||||
"504": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RepositoryApiError"
|
||||
}
|
||||
},
|
||||
"application/problem+json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/StartupProblemDetails"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Error response"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-companion-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:afde3903f29b9087390b69f9761a935cd5004c4dab17ce684f4149c4abae89c4)
|
||||
// Input contract: 0.1.0 (sha256:2dad54b31293643566259ea555e11c46effce33aab9f30c8a861a46303024bad)
|
||||
// Options: companion-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/companion-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_companion_api_types -- --check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-repository-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:afde3903f29b9087390b69f9761a935cd5004c4dab17ce684f4149c4abae89c4)
|
||||
// Input contract: 0.1.0 (sha256:2dad54b31293643566259ea555e11c46effce33aab9f30c8a861a46303024bad)
|
||||
// Options: repository-operation-schema-closure,openapi-3.1,format:uint64=safe-number,strict-safe-integers,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/repository-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_repository_openapi_types -- --check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-runtime-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:afde3903f29b9087390b69f9761a935cd5004c4dab17ce684f4149c4abae89c4)
|
||||
// Input contract: 0.1.0 (sha256:2dad54b31293643566259ea555e11c46effce33aab9f30c8a861a46303024bad)
|
||||
// Options: runtime-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/runtime-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_runtime_api_types -- --check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-ticket-browser-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:afde3903f29b9087390b69f9761a935cd5004c4dab17ce684f4149c4abae89c4)
|
||||
// Input contract: 0.1.0 (sha256:2dad54b31293643566259ea555e11c46effce33aab9f30c8a861a46303024bad)
|
||||
// Options: ticket-objective-merge-request-browser-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/ticket-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_ticket_api_types -- --check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-workdir-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:afde3903f29b9087390b69f9761a935cd5004c4dab17ce684f4149c4abae89c4)
|
||||
// Input contract: 0.1.0 (sha256:2dad54b31293643566259ea555e11c46effce33aab9f30c8a861a46303024bad)
|
||||
// Options: workdir-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/workdir-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_workdir_api_types -- --check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @generated by yoi-worker-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:afde3903f29b9087390b69f9761a935cd5004c4dab17ce684f4149c4abae89c4)
|
||||
// Input contract: 0.1.0 (sha256:2dad54b31293643566259ea555e11c46effce33aab9f30c8a861a46303024bad)
|
||||
// Options: worker-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/worker-launch-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_worker_launch_api_types -- --check
|
||||
|
||||
Reference in New Issue
Block a user