assay_workflow/api/
namespaces.rs1use std::sync::Arc;
2
3use axum::extract::{Path, State};
4use axum::routing::{get, post};
5use axum::{Json, Router};
6use serde::Deserialize;
7use utoipa::ToSchema;
8
9use crate::api::workflows::AppError;
10use crate::api::AppState;
11use crate::store::WorkflowStore;
12
13pub fn router<S: WorkflowStore + 'static>() -> Router<Arc<AppState<S>>> {
14 Router::new()
15 .route("/namespaces", post(create_namespace).get(list_namespaces))
16 .route(
17 "/namespaces/{name}",
18 get(get_namespace_stats).delete(delete_namespace),
19 )
20}
21
22#[derive(Deserialize, ToSchema)]
23pub struct CreateNamespaceRequest {
24 pub name: String,
25}
26
27#[utoipa::path(
28 post, path = "/api/v1/namespaces",
29 tag = "namespaces",
30 request_body = CreateNamespaceRequest,
31 responses(
32 (status = 201, description = "Namespace created"),
33 (status = 500, description = "Internal error"),
34 ),
35)]
36pub async fn create_namespace<S: WorkflowStore>(
37 State(state): State<Arc<AppState<S>>>,
38 Json(req): Json<CreateNamespaceRequest>,
39) -> Result<axum::http::StatusCode, AppError> {
40 state.engine.create_namespace(&req.name).await?;
41 Ok(axum::http::StatusCode::CREATED)
42}
43
44#[utoipa::path(
45 get, path = "/api/v1/namespaces",
46 tag = "namespaces",
47 responses(
48 (status = 200, description = "List of namespaces", body = Vec<NamespaceRecord>),
49 ),
50)]
51pub async fn list_namespaces<S: WorkflowStore>(
52 State(state): State<Arc<AppState<S>>>,
53) -> Result<Json<Vec<serde_json::Value>>, AppError> {
54 let namespaces = state.engine.list_namespaces().await?;
55 let json: Vec<serde_json::Value> = namespaces
56 .into_iter()
57 .map(|n| serde_json::to_value(n).unwrap_or_default())
58 .collect();
59 Ok(Json(json))
60}
61
62#[utoipa::path(
63 get, path = "/api/v1/namespaces/{name}",
64 tag = "namespaces",
65 params(("name" = String, Path, description = "Namespace name")),
66 responses(
67 (status = 200, description = "Namespace statistics", body = NamespaceStats),
68 ),
69)]
70pub async fn get_namespace_stats<S: WorkflowStore>(
71 State(state): State<Arc<AppState<S>>>,
72 Path(name): Path<String>,
73) -> Result<Json<serde_json::Value>, AppError> {
74 let stats = state.engine.get_namespace_stats(&name).await?;
75 Ok(Json(serde_json::to_value(stats)?))
76}
77
78#[utoipa::path(
79 delete, path = "/api/v1/namespaces/{name}",
80 tag = "namespaces",
81 params(("name" = String, Path, description = "Namespace name")),
82 responses(
83 (status = 200, description = "Namespace deleted"),
84 (status = 404, description = "Namespace not found or is 'main'"),
85 ),
86)]
87pub async fn delete_namespace<S: WorkflowStore>(
88 State(state): State<Arc<AppState<S>>>,
89 Path(name): Path<String>,
90) -> Result<axum::http::StatusCode, AppError> {
91 if name == "main" {
92 return Err(AppError::NotFound(
93 "cannot delete the 'main' namespace".to_string(),
94 ));
95 }
96 let deleted = state.engine.delete_namespace(&name).await?;
97 if deleted {
98 Ok(axum::http::StatusCode::OK)
99 } else {
100 Err(AppError::NotFound(format!("namespace {name}")))
101 }
102}
103
104use crate::store::{NamespaceRecord, NamespaceStats};