Skip to main content

assay_workflow/api/
openapi.rs

1use axum::Router;
2use axum::http::{StatusCode, header};
3use axum::response::{Html, IntoResponse};
4use axum::routing::get;
5use std::sync::Arc;
6use utoipa::OpenApi;
7
8use crate::ctx::WorkflowCtx;
9use crate::store::WorkflowStore;
10
11/// Build the OpenAPI specification for the workflow engine.
12#[derive(OpenApi)]
13#[openapi(
14    info(
15        title = "assay-workflow API",
16        version = "0.1.0",
17        description = "Durable workflow engine with REST+SSE API. Language-agnostic — any HTTP client can start workflows, execute activities, send signals.",
18        license(name = "Apache-2.0"),
19    ),
20    paths(
21        crate::api::workflows::start_workflow,
22        crate::api::workflows::list_workflows,
23        crate::api::workflows::describe_workflow,
24        crate::api::workflows::get_events,
25        crate::api::workflows::send_signal,
26        crate::api::workflows::cancel_workflow,
27        crate::api::workflows::terminate_workflow,
28        crate::api::workflows::retry_failed_activity,
29        crate::api::tasks::register_worker,
30        crate::api::tasks::poll_task,
31        crate::api::tasks::complete_task,
32        crate::api::tasks::fail_task,
33        crate::api::tasks::heartbeat_task,
34        crate::api::tasks::worker_heartbeat,
35        crate::api::schedules::create_schedule,
36        crate::api::schedules::list_schedules,
37        crate::api::schedules::get_schedule,
38        crate::api::schedules::delete_schedule,
39        crate::api::schedules::patch_schedule,
40        crate::api::schedules::pause_schedule,
41        crate::api::schedules::resume_schedule,
42        crate::api::workflows::list_children,
43        crate::api::workflows::continue_as_new,
44        crate::api::workflows::get_workflow_state,
45        crate::api::workflows::get_workflow_state_by_name,
46        crate::api::workers::list_workers,
47        crate::api::public::health_check,
48        crate::api::public::version,
49    ),
50    components(schemas(
51        crate::types::WorkflowRecord,
52        crate::types::WorkflowEvent,
53        crate::types::WorkflowActivity,
54        crate::types::WorkflowTimer,
55        crate::types::WorkflowSignal,
56        crate::types::WorkflowSchedule,
57        crate::types::WorkflowWorker,
58        crate::types::WorkflowStatus,
59        crate::types::ActivityStatus,
60        crate::api::workflows::StartWorkflowRequest,
61        crate::api::workflows::WorkflowResponse,
62        crate::api::tasks::RegisterWorkerRequest,
63        crate::api::tasks::RegisterWorkerResponse,
64        crate::api::tasks::PollRequest,
65        crate::api::tasks::CompleteTaskBody,
66        crate::api::tasks::FailTaskBody,
67        crate::api::schedules::CreateScheduleRequest,
68        crate::api::schedules::PatchScheduleRequest,
69        crate::api::workflows::ContinueAsNewBody,
70        crate::api::workflows::RetryFailedActivityBody,
71        crate::api::workflows::RetryFailedActivityResponse,
72        crate::api::public::VersionInfo,
73    )),
74    tags(
75        (name = "workflows", description = "Workflow lifecycle management"),
76        (name = "tasks", description = "Task execution for worker apps"),
77        (name = "schedules", description = "Cron schedule management"),
78        (name = "workers", description = "Worker registry and health"),
79        (name = "events", description = "Real-time event streams (SSE)"),
80        (name = "meta", description = "Engine metadata (version, build info)"),
81    ),
82    servers(
83        (url = "/", description = "Current server"),
84    ),
85)]
86pub struct ApiDoc;
87
88pub fn router<S: WorkflowStore + 'static>() -> Router<Arc<WorkflowCtx<S>>> {
89    Router::new()
90        .route("/api/v1/engine/workflow/openapi.json", get(openapi_json))
91        .route("/api/v1/engine/workflow/docs", get(docs_page))
92}
93
94async fn openapi_json() -> impl IntoResponse {
95    let spec = ApiDoc::openapi().to_json().unwrap_or_default();
96    (
97        StatusCode::OK,
98        [(header::CONTENT_TYPE, "application/json")],
99        spec,
100    )
101}
102
103/// Lightweight API docs page using Scalar (loaded from CDN, ~50KB).
104async fn docs_page() -> Html<String> {
105    let html = r#"<!DOCTYPE html>
106<html>
107<head>
108    <title>assay-workflow API</title>
109    <meta charset="utf-8">
110    <meta name="viewport" content="width=device-width, initial-scale=1">
111</head>
112<body>
113    <script id="api-reference" data-url="/api/v1/engine/workflow/openapi.json"></script>
114    <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
115</body>
116</html>"#;
117    Html(html.to_string())
118}