Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions beacon-api/src/axum/admin/extensions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//! Admin endpoints for managing a table's downstream extensions.

use std::sync::Arc;

use ::axum::{
extract::{Path, State},
http::StatusCode,
Json,
};
use beacon_core::api::TableExtensions;
use beacon_core::runtime::Runtime;

use super::bad_request;

/// Replaces the named table's extensions document (MCP descriptor, query
/// presets). The document is validated against the table schema; an empty body
/// (`{}`) clears all extensions.
#[tracing::instrument(level = "info", skip(state, extensions))]
#[utoipa::path(
tag = "admin",
put,
path = "/api/admin/table-extensions/{table_name}",
params(("table_name" = String, Path, description = "Registered table name")),
request_body = TableExtensions,
responses(
(status = 200, description = "Extensions updated"),
(status = 400, description = "Invalid request, validation failed, or table not found")
),
security(("basic-auth" = []))
)]
pub(crate) async fn set_table_extensions(
State(state): State<Arc<Runtime>>,
Path(table_name): Path<String>,
Json(extensions): Json<TableExtensions>,
) -> Result<(), (StatusCode, String)> {
state
.set_table_extensions(table_name, extensions)
.await
.map_err(bad_request)
}

/// Removes all extensions from the named table.
#[tracing::instrument(level = "info", skip(state))]
#[utoipa::path(
tag = "admin",
delete,
path = "/api/admin/table-extensions/{table_name}",
params(("table_name" = String, Path, description = "Registered table name")),
responses(
(status = 200, description = "Extensions removed"),
(status = 400, description = "Table not found or removal failed")
),
security(("basic-auth" = []))
)]
pub(crate) async fn delete_table_extensions(
State(state): State<Arc<Runtime>>,
Path(table_name): Path<String>,
) -> Result<(), (StatusCode, String)> {
state
.delete_table_extensions(table_name)
.await
.map_err(bad_request)
}
5 changes: 5 additions & 0 deletions beacon-api/src/axum/admin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::axum::auth::basic_auth;

mod check;
mod crawlers;
mod extensions;
mod external_tables;

/// OpenAPI document marker for the admin surface.
Expand All @@ -37,6 +38,10 @@ pub(crate) fn setup_admin_router() -> (Router<Arc<Runtime>>, utoipa::openapi::Op
))
.routes(routes!(crawlers::run_crawler))
.routes(routes!(external_tables::create_external_table))
.routes(routes!(
extensions::set_table_extensions,
extensions::delete_table_extensions
))
.layer(::axum::middleware::from_fn(basic_auth))
.split_for_parts();

Expand Down
1 change: 1 addition & 0 deletions beacon-api/src/axum/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub(crate) fn setup_client_router() -> (Router<Arc<Runtime>>, utoipa::openapi::O
.routes(routes!(tables::list_tables_with_schema))
.routes(routes!(tables::default_table))
.routes(routes!(tables::list_table_schema))
.routes(routes!(tables::list_table_extensions))
.routes(routes!(tables::list_table_config))
.routes(routes!(tables::default_table_schema))
.routes(routes!(functions::list_functions))
Expand Down
44 changes: 43 additions & 1 deletion beacon-api/src/axum/client/tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use ::axum::{
http::StatusCode,
Json,
};
use beacon_core::api::{SchemaFieldView, SchemaView, TableConfigView};
use beacon_core::api::{SchemaFieldView, SchemaView, TableConfigView, TableExtensions};
use beacon_core::runtime::Runtime;
use utoipa::{IntoParams, ToSchema};

Expand Down Expand Up @@ -152,6 +152,48 @@ pub(crate) async fn list_table_config(
}
}

/// Query parameters for [`list_table_extensions`].
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, IntoParams)]
pub struct ListTableExtensionsQuery {
/// Name of the registered table whose extensions to return.
pub table_name: String,
}

/// Returns the downstream extensions (MCP descriptor, query presets) attached to
/// the named table, or 404 if the table is not registered. A table with no
/// extensions returns an empty object.
#[tracing::instrument(level = "info", skip(state))]
#[utoipa::path(
tag = "tables",
get,
path = "/api/table-extensions",
params(ListTableExtensionsQuery),
responses(
(status = 200, description = "The table's extensions", body = TableExtensions),
(status = 404, description = "Table not found"),
),
security(
Comment on lines +171 to +175
(),
("basic-auth" = []),
("bearer" = [])
)
)]
pub(crate) async fn list_table_extensions(
State(state): State<Arc<Runtime>>,
Query(query): Query<ListTableExtensionsQuery>,
) -> Result<Json<TableExtensions>, (StatusCode, String)> {
match state.get_table_extensions(query.table_name.clone()).await {
Ok(extensions) => Ok(Json(extensions)),
Err(error) => {
tracing::error!(?error, "error listing table extensions");
Err((
StatusCode::NOT_FOUND,
format!("Table {} not found", query.table_name),
))
}
}
}
Comment on lines +185 to +195

/// Returns the Arrow schema of the runtime's default table.
#[tracing::instrument(level = "info", skip(state))]
#[utoipa::path(
Expand Down
5 changes: 5 additions & 0 deletions beacon-core/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ use crate::metrics::ConsolidatedMetrics;
use serde_json::{Map, Value};
use utoipa::ToSchema;

/// Re-exported typed table-extension contracts (see [`crate::extensions`]).
pub use crate::extensions::{
McpExtension, Preset, PresetExtension, PresetFilter, TableExtensions,
};

/// A single parameter of a registered function.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct FunctionParameterInfo {
Expand Down
Loading
Loading