use axum::{
Json,
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
};
use fraiseql_core::db::traits::DatabaseAdapter;
use serde::{Deserialize, Serialize};
use crate::routes::graphql::app_state::AppState;
#[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<A>(
Path(entity): Path<String>,
State(state): State<AppState<A>>,
Json(req): Json<DataBrowserQuery>,
) -> impl IntoResponse
where
A: DatabaseAdapter + Clone + Send + Sync + 'static,
{
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();
}
Json(DataQueryResponse {
rows: Vec::new(),
total: 0,
page: req.page,
page_size: req.page_size,
})
.into_response()
}
pub async fn mutate_handler<A>(
Path(entity): Path<String>,
State(state): State<AppState<A>>,
Json(_req): Json<DataMutateRequest>,
) -> impl IntoResponse
where
A: DatabaseAdapter + Clone + Send + Sync + 'static,
{
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();
}
Json(serde_json::json!({"success": true})).into_response()
}