fraiseql-server 2.9.0

HTTP server for FraiseQL v2 GraphQL engine
Documentation
//! Axum route definitions for observer management.

use axum::{
    Router,
    routing::{get, post},
};

use super::{
    changelog_handlers::{
        ChangelogState, changelog_list_handler, checkpoint_get_handler, checkpoint_save_handler,
    },
    dlq_handlers::{
        DlqState, delivery_health_handler, dlq_delete_handler, dlq_get_handler, dlq_list_handler,
        dlq_retry_all_handler, dlq_retry_handler, dlq_stats_handler,
    },
    handlers::{
        ObserverState, RuntimeHealthState, create_observer, delete_observer, disable_observer,
        enable_observer, get_observer, get_observer_stats, get_runtime_health, list_observer_logs,
        list_observers, reload_observers, update_observer,
    },
};

/// Create the observer management router.
///
/// # Routes
///
/// - `GET    /`           - List all observers
/// - `POST   /`           - Create a new observer
/// - `GET    /stats`      - Get statistics for all observers
/// - `GET    /logs`       - List execution logs for all observers
/// - `GET    /{id}`        - Get a specific observer
/// - `PATCH  /{id}`        - Update a specific observer
/// - `DELETE /{id}`        - Delete a specific observer (soft delete)
/// - `POST   /{id}/enable` - Enable a specific observer
/// - `POST   /{id}/disable`- Disable a specific observer
/// - `GET    /{id}/stats`  - Get statistics for a specific observer
/// - `GET    /{id}/logs`   - List execution logs for a specific observer
pub fn observer_routes(state: ObserverState) -> Router {
    Router::new()
        // Collection routes
        .route("/", get(list_observers).post(create_observer))
        .route("/stats", get(get_all_stats))
        .route("/logs", get(list_all_logs))
        // Individual observer routes
        .route("/{id}", get(get_observer).patch(update_observer).delete(delete_observer))
        .route("/{id}/enable", post(enable_observer))
        .route("/{id}/disable", post(disable_observer))
        .route("/{id}/stats", get(get_single_stats))
        .route("/{id}/logs", get(list_single_logs))
        .with_state(state)
}

/// Get stats for all observers (wrapper to pass None for `observer_id`).
async fn get_all_stats(
    state: axum::extract::State<ObserverState>,
) -> impl axum::response::IntoResponse {
    get_observer_stats(state, None).await
}

/// Get stats for a single observer.
async fn get_single_stats(
    state: axum::extract::State<ObserverState>,
    path: axum::extract::Path<uuid::Uuid>,
) -> impl axum::response::IntoResponse {
    get_observer_stats(state, Some(path)).await
}

/// List logs for all observers.
async fn list_all_logs(
    state: axum::extract::State<ObserverState>,
    query: axum::extract::Query<super::ListObserverLogsQuery>,
) -> impl axum::response::IntoResponse {
    list_observer_logs(state, axum::extract::Path(None), query).await
}

/// List logs for a single observer.
async fn list_single_logs(
    state: axum::extract::State<ObserverState>,
    path: axum::extract::Path<uuid::Uuid>,
    query: axum::extract::Query<super::ListObserverLogsQuery>,
) -> impl axum::response::IntoResponse {
    list_observer_logs(state, axum::extract::Path(Some(path.0)), query).await
}

/// Create the observer runtime health router.
///
/// # Routes
///
/// - `GET  /runtime/health` - Get runtime health status
/// - `POST /runtime/reload` - Reload observers from database
pub fn observer_runtime_routes(state: RuntimeHealthState) -> Router {
    Router::new()
        .route("/runtime/health", get(get_runtime_health))
        .route("/runtime/reload", post(reload_observers))
        .with_state(state)
}

/// Create the DLQ (Dead Letter Queue) delivery status router.
///
/// # Routes
///
/// - `GET    /delivery/health`     - Observer delivery health summary
/// - `GET    /dlq`                 - List DLQ items (paginated, filterable)
/// - `GET    /dlq/stats`           - Aggregate DLQ statistics
/// - `GET    /dlq/{id}`            - Get a single DLQ item
/// - `DELETE /dlq/{id}`            - Remove a single DLQ item
/// - `POST   /dlq/{id}/retry`      - Re-process a single DLQ item
/// - `POST   /dlq/retry-all`       - Re-process all DLQ items
pub fn observer_dlq_routes(state: DlqState) -> Router {
    Router::new()
        .route("/delivery/health", get(delivery_health_handler))
        .route("/dlq", get(dlq_list_handler))
        .route("/dlq/stats", get(dlq_stats_handler))
        .route("/dlq/retry-all", post(dlq_retry_all_handler))
        // Static `/dlq/stats` above takes priority over this capture in axum.
        .route("/dlq/{id}", get(dlq_get_handler).delete(dlq_delete_handler))
        .route("/dlq/{id}/retry", post(dlq_retry_handler))
        .with_state(state)
}

/// Create the changelog and checkpoint router.
///
/// # Routes
///
/// - `GET /changelog`                  - Poll changelog entries (paginated, filterable)
/// - `GET /checkpoint/{listener_id}`   - Read a listener's checkpoint
/// - `PUT /checkpoint/{listener_id}`   - Save / update a listener's checkpoint
pub fn observer_changelog_routes(state: ChangelogState) -> Router {
    Router::new()
        .route("/changelog", get(changelog_list_handler))
        .route(
            "/checkpoint/{listener_id}",
            get(checkpoint_get_handler).put(checkpoint_save_handler),
        )
        .with_state(state)
}