assay_workflow/api/
openapi.rs1use axum::http::{header, StatusCode};
2use axum::response::{Html, IntoResponse};
3use axum::routing::get;
4use axum::Router;
5use std::sync::Arc;
6use utoipa::OpenApi;
7
8use crate::api::AppState;
9use crate::store::WorkflowStore;
10
11#[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::tasks::register_worker,
29 crate::api::tasks::poll_task,
30 crate::api::tasks::complete_task,
31 crate::api::tasks::fail_task,
32 crate::api::tasks::heartbeat_task,
33 crate::api::tasks::worker_heartbeat,
34 crate::api::schedules::create_schedule,
35 crate::api::schedules::list_schedules,
36 crate::api::schedules::get_schedule,
37 crate::api::schedules::delete_schedule,
38 crate::api::schedules::patch_schedule,
39 crate::api::schedules::pause_schedule,
40 crate::api::schedules::resume_schedule,
41 crate::api::workflows::list_children,
42 crate::api::workflows::continue_as_new,
43 crate::api::workflows::get_workflow_state,
44 crate::api::workflows::get_workflow_state_by_name,
45 crate::api::workers::list_workers,
46 crate::api::public::health_check,
47 crate::api::public::version,
48 ),
49 components(schemas(
50 crate::types::WorkflowRecord,
51 crate::types::WorkflowEvent,
52 crate::types::WorkflowActivity,
53 crate::types::WorkflowTimer,
54 crate::types::WorkflowSignal,
55 crate::types::WorkflowSchedule,
56 crate::types::WorkflowWorker,
57 crate::types::WorkflowStatus,
58 crate::types::ActivityStatus,
59 crate::api::workflows::StartWorkflowRequest,
60 crate::api::workflows::WorkflowResponse,
61 crate::api::tasks::RegisterWorkerRequest,
62 crate::api::tasks::RegisterWorkerResponse,
63 crate::api::tasks::PollRequest,
64 crate::api::tasks::CompleteTaskBody,
65 crate::api::tasks::FailTaskBody,
66 crate::api::schedules::CreateScheduleRequest,
67 crate::api::schedules::PatchScheduleRequest,
68 crate::api::workflows::ContinueAsNewBody,
69 crate::api::public::VersionInfo,
70 )),
71 tags(
72 (name = "workflows", description = "Workflow lifecycle management"),
73 (name = "tasks", description = "Task execution for worker apps"),
74 (name = "schedules", description = "Cron schedule management"),
75 (name = "workers", description = "Worker registry and health"),
76 (name = "events", description = "Real-time event streams (SSE)"),
77 (name = "meta", description = "Engine metadata (version, build info)"),
78 ),
79 servers(
80 (url = "/", description = "Current server"),
81 ),
82)]
83pub struct ApiDoc;
84
85pub fn router<S: WorkflowStore + 'static>() -> Router<Arc<AppState<S>>> {
86 Router::new()
87 .route("/api/v1/openapi.json", get(openapi_json))
88 .route("/api/v1/docs", get(docs_page))
89}
90
91async fn openapi_json() -> impl IntoResponse {
92 let spec = ApiDoc::openapi().to_json().unwrap_or_default();
93 (
94 StatusCode::OK,
95 [(header::CONTENT_TYPE, "application/json")],
96 spec,
97 )
98}
99
100async fn docs_page() -> Html<String> {
102 let html = r#"<!DOCTYPE html>
103<html>
104<head>
105 <title>assay-workflow API</title>
106 <meta charset="utf-8">
107 <meta name="viewport" content="width=device-width, initial-scale=1">
108</head>
109<body>
110 <script id="api-reference" data-url="/api/v1/openapi.json"></script>
111 <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
112</body>
113</html>"#;
114 Html(html.to_string())
115}