Skip to main content

mina_node_native/http_server/routes/
state.rs

1//! State inspection endpoints.
2//!
3//! - `GET /state` - Get node state with optional JSONPath filter
4//! - `POST /state` - Get node state with JSONPath filter in body
5//! - `GET /state/peers` - Get connected peers
6//! - `GET /state/message-progress` - Get message progress
7//!
8//! The JSONPath filter implementation is in `impl mina_node::rpc_effectful::RpcService for NodeService`.
9
10use axum::{
11    extract::{Query, State},
12    Json,
13};
14use serde::Deserialize;
15use utoipa_axum::{router::OpenApiRouter, routes};
16
17use mina_node::rpc::{RpcMessageProgressResponse, RpcPeersGetResponse, RpcRequest};
18use mina_node_common::rpc::RpcStateGetResponse;
19
20use crate::http_server::{AppError, AppResult, AppState, JsonErrorResponse};
21
22/// State routes
23pub fn routes() -> OpenApiRouter<AppState> {
24    OpenApiRouter::new()
25        .routes(routes!(state_get, state_post))
26        .routes(routes!(peers))
27        .routes(routes!(message_progress))
28}
29
30#[derive(Deserialize, Default)]
31struct StateQueryParams {
32    /// Optional JSONPath filter expression.
33    filter: Option<String>,
34}
35
36/// Node state with optional JSONPath filter (query param)
37#[utoipa::path(
38    get,
39    path = "/state",
40    tag = "state",
41    params(
42        ("filter" = Option<String>, Query, description = "JSONPath filter expression")
43    ),
44    responses(
45        (status = 200, description = "Node state"),
46        (status = 400, description = "Invalid filter expression", body = JsonErrorResponse,
47            example = json!({"error": "failed to parse filter expression: unexpected token"}))
48    )
49)]
50async fn state_get(
51    State(state): State<AppState>,
52    Query(params): Query<StateQueryParams>,
53) -> AppResult<Json<serde_json::Value>> {
54    state_handler(state, params.filter).await
55}
56
57/// Node state with JSONPath filter in body
58#[utoipa::path(
59    post,
60    path = "/state",
61    tag = "state",
62    responses(
63        (status = 200, description = "Node state"),
64        (status = 400, description = "Invalid filter expression", body = JsonErrorResponse,
65            example = json!({"error": "failed to parse filter expression: unexpected token"}))
66    )
67)]
68async fn state_post(
69    State(state): State<AppState>,
70    Json(params): Json<StateQueryParams>,
71) -> AppResult<Json<serde_json::Value>> {
72    state_handler(state, params.filter).await
73}
74
75/// Shared handler for state requests
76async fn state_handler(
77    state: AppState,
78    filter: Option<String>,
79) -> AppResult<Json<serde_json::Value>> {
80    let result: Option<RpcStateGetResponse> = state
81        .rpc_sender()
82        .oneshot_request(RpcRequest::StateGet(filter))
83        .await;
84
85    match result {
86        None => Err(AppError::ChannelDropped),
87        Some(Ok(value)) => Ok(Json(value)),
88        Some(Err(err)) => Err(AppError::BadRequest(err.to_string())),
89    }
90}
91
92/// Connected peers
93#[utoipa::path(
94    get,
95    path = "/state/peers",
96    tag = "state",
97    responses(
98        // inline: RpcPeersGetResponse is a type alias for Vec<T>, which would register as "Vec"
99        (status = 200, description = "Connected peers", body = inline(RpcPeersGetResponse))
100    )
101)]
102async fn peers(State(state): State<AppState>) -> AppResult<Json<RpcPeersGetResponse>> {
103    jsonify_rpc!(state, RpcRequest::PeersGet)
104}
105
106/// Message progress
107#[utoipa::path(
108    get,
109    path = "/state/message-progress",
110    tag = "state",
111    responses(
112        (status = 200, description = "Message progress information", body = RpcMessageProgressResponse)
113    )
114)]
115async fn message_progress(
116    State(state): State<AppState>,
117) -> AppResult<Json<RpcMessageProgressResponse>> {
118    jsonify_rpc!(state, RpcRequest::MessageProgressGet)
119}