use axum::{
Json,
extract::{Path, State},
http::StatusCode,
response::{IntoResponse, Response},
};
use serde::{Deserialize, Serialize};
use crate::routes::{graphql::app_state::AppState, studio::not_implemented};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum FilterOp {
Eq,
Ne,
Lt,
Lte,
Gt,
Gte,
Contains,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum SortDir {
Asc,
Desc,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FilterClause {
pub field: String,
pub op: FilterOp,
pub value: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SortClause {
pub field: String,
pub dir: SortDir,
}
const fn default_page() -> u32 {
1
}
const fn default_page_size() -> u32 {
50
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataBrowserQuery {
#[serde(default = "default_page")]
pub page: u32,
#[serde(default = "default_page_size")]
pub page_size: u32,
#[serde(default)]
pub filter: Vec<FilterClause>,
#[serde(default)]
pub sort: Vec<SortClause>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum MutateOperation {
Insert,
Update,
Delete,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataMutateRequest {
pub operation: MutateOperation,
pub data: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataQueryResponse {
pub rows: Vec<serde_json::Value>,
pub total: u64,
pub page: u32,
pub page_size: u32,
}
pub async fn query_handler(
Path(entity): Path<String>,
State(state): State<AppState>,
Json(_req): Json<DataBrowserQuery>,
) -> Response {
let schema = state.executor.load().schema().clone();
let entity_exists = schema.types.iter().any(|t| t.name == entity);
if !entity_exists {
return (
StatusCode::NOT_FOUND,
Json(serde_json::json!({
"error": "Not Found",
"message": format!("Entity '{entity}' does not exist in the compiled schema")
})),
)
.into_response();
}
not_implemented(
"studio.data.query",
"The data browser's query path is not wired to the executor; it cannot read \
rows. Query the entity through /graphql.",
)
}
pub async fn mutate_handler(
Path(entity): Path<String>,
State(state): State<AppState>,
Json(_req): Json<DataMutateRequest>,
) -> Response {
let schema = state.executor.load().schema().clone();
let entity_exists = schema.types.iter().any(|t| t.name == entity);
if !entity_exists {
return (
StatusCode::NOT_FOUND,
Json(serde_json::json!({
"error": "Not Found",
"message": format!("Entity '{entity}' does not exist in the compiled schema")
})),
)
.into_response();
}
not_implemented(
"studio.data.mutate",
"The data browser's mutation path is not wired to the executor; no row was \
inserted, updated or deleted. Use a declared GraphQL mutation.",
)
}