Skip to main content

assay_workflow/api/
namespaces.rs

1use 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};