use std::sync::Arc;
use axum::{
extract::{Path, State},
http::StatusCode,
routing::{delete, get, put, post},
Json, Router,
};
use relay_core_api::rule::Rule;
use relay_core_runtime::{CoreState, audit::AuditActor};
use relay_core_runtime::rule::MockResponseRuleConfig;
use serde::Deserialize;
use serde_json::Value;
use uuid::Uuid;
pub fn router(state: Arc<CoreState>) -> Router {
Router::new()
.route("/api/v1/rules", get(list_rules))
.route("/api/v1/rules", put(set_rule))
.route("/api/v1/rules/{id}", delete(delete_rule))
.route("/api/v1/mock", post(mock_url))
.with_state(state)
}
async fn list_rules(State(state): State<Arc<CoreState>>) -> Json<Value> {
let rules = state.get_rules().await;
Json(serde_json::to_value(&rules).unwrap_or_default())
}
async fn set_rule(
State(state): State<Arc<CoreState>>,
Json(rule_val): Json<Value>,
) -> Result<Json<Value>, (StatusCode, String)> {
let rule: Rule = serde_json::from_value(rule_val)
.map_err(|e| (StatusCode::BAD_REQUEST, format!("Invalid rule JSON: {}", e)))?;
let rule_id = rule.id.clone();
state
.upsert_rule_from(
AuditActor::Http,
"rule.upsert",
rule_id.clone(),
serde_json::json!({ "route": "/api/v1/rules" }),
rule,
)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e))?;
Ok(Json(serde_json::json!({ "id": rule_id, "status": "ok" })))
}
async fn delete_rule(
State(state): State<Arc<CoreState>>,
Path(id): Path<String>,
) -> Result<Json<Value>, StatusCode> {
let deleted = state
.delete_rule_from(
AuditActor::Http,
"rule.delete",
id.clone(),
serde_json::json!({ "route": "/api/v1/rules/{id}" }),
&id,
)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
if !deleted {
return Err(StatusCode::NOT_FOUND);
}
Ok(Json(serde_json::json!({ "id": id, "status": "deleted" })))
}
#[derive(Debug, Deserialize)]
struct MockRequest {
url_pattern: String,
status: u16,
#[serde(default)]
body: String,
#[serde(default = "default_content_type")]
content_type: String,
}
fn default_content_type() -> String {
"application/json".to_string()
}
async fn mock_url(
State(state): State<Arc<CoreState>>,
Json(req): Json<MockRequest>,
) -> Result<Json<Value>, (StatusCode, String)> {
let rule_id = format!("api-mock-{}", Uuid::new_v4());
state
.create_mock_response_rule_from(
AuditActor::Http,
rule_id.clone(),
serde_json::json!({
"route": "/api/v1/mock",
"url_pattern": req.url_pattern,
"status": req.status
}),
MockResponseRuleConfig {
rule_id: rule_id.clone(),
url_pattern: req.url_pattern.clone(),
name: format!("api-mock:{}", req.url_pattern),
status: req.status,
content_type: req.content_type,
body: req.body,
},
)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e))?;
Ok(Json(serde_json::json!({
"rule_id": rule_id,
"url_pattern": req.url_pattern,
"status": req.status
})))
}