use axum::{extract::State, response::Json};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::{json, Value};
pub trait FailureDesignerProvider: Clone + Send + Sync + 'static {
type Rule: DeserializeOwned + Serialize + Send + Sync + 'static;
type Scenario: Serialize;
fn validate_rule(&self, rule: &Self::Rule) -> Result<(), String>;
fn rule_to_scenario(&self, rule: &Self::Rule) -> Result<Self::Scenario, String>;
fn generate_route_chaos_config(&self, rule: &Self::Rule) -> Result<Value, String>;
fn webhook_hook(&self, rule: &Self::Rule) -> Result<Option<Value>, String>;
}
#[derive(Debug, Deserialize)]
pub struct ValidateRuleRequest<R> {
pub rule: R,
}
#[derive(Debug, Deserialize)]
pub struct GenerateScenarioRequest<R> {
pub rule: R,
}
#[derive(Debug, Serialize)]
pub struct ValidateRuleResponse {
pub success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Debug, Serialize)]
pub struct GenerateScenarioResponse<C> {
pub success: bool,
pub scenario: C,
pub route_chaos_config: Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub webhook_hook: Option<Value>,
}
pub async fn validate_rule<S: FailureDesignerProvider>(
State(state): State<S>,
Json(request): Json<ValidateRuleRequest<S::Rule>>,
) -> Json<Value> {
let designer = &state;
match designer.validate_rule(&request.rule) {
Ok(()) => Json(json!({
"success": true,
})),
Err(e) => Json(json!({
"success": false,
"error": e,
})),
}
}
pub async fn generate_scenario<S: FailureDesignerProvider>(
State(state): State<S>,
Json(request): Json<GenerateScenarioRequest<S::Rule>>,
) -> Result<Json<Value>, String> {
let designer = &state;
let scenario = designer
.rule_to_scenario(&request.rule)
.map_err(|e| format!("Failed to generate scenario: {}", e))?;
let route_chaos_config = designer
.generate_route_chaos_config(&request.rule)
.map_err(|e| format!("Failed to generate route chaos config: {}", e))?;
let webhook_hook = designer
.webhook_hook(&request.rule)
.map_err(|e| format!("Failed to generate webhook hook: {}", e))?;
Ok(Json(json!({
"success": true,
"scenario": scenario,
"route_chaos_config": route_chaos_config,
"webhook_hook": webhook_hook,
})))
}
pub async fn preview_config<S: FailureDesignerProvider>(
State(state): State<S>,
Json(request): Json<GenerateScenarioRequest<S::Rule>>,
) -> Result<Json<Value>, String> {
let designer = &state;
designer
.validate_rule(&request.rule)
.map_err(|e| format!("Validation failed: {}", e))?;
let scenario = designer
.rule_to_scenario(&request.rule)
.map_err(|e| format!("Failed to generate scenario: {}", e))?;
let route_chaos_config = designer
.generate_route_chaos_config(&request.rule)
.map_err(|e| format!("Failed to generate route chaos config: {}", e))?;
let webhook_hook = designer
.webhook_hook(&request.rule)
.map_err(|e| format!("Failed to generate webhook hook: {}", e))?;
Ok(Json(json!({
"success": true,
"rule": request.rule,
"scenario": scenario,
"route_chaos_config": route_chaos_config,
"webhook_hook": webhook_hook,
})))
}