mockforge_intelligence/handlers/drift_budget/
scoped_budgets.rs1use super::DriftBudgetSource;
4use super::DriftBudgetStateProvider;
5use axum::{extract::State, http::StatusCode, response::Json};
6use mockforge_contracts::contract_drift::types::DriftBudget;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Deserialize, Serialize)]
11pub struct CreateWorkspaceBudgetRequest {
12 pub workspace_id: String,
14 pub max_breaking_changes: Option<u32>,
16 pub max_non_breaking_changes: Option<u32>,
18 pub max_field_churn_percent: Option<f64>,
20 pub time_window_days: Option<u32>,
22 pub enabled: Option<bool>,
24}
25
26#[derive(Debug, Deserialize, Serialize)]
28pub struct CreateServiceBudgetRequest {
29 pub service_name: String,
31 pub max_breaking_changes: Option<u32>,
33 pub max_non_breaking_changes: Option<u32>,
35 pub max_field_churn_percent: Option<f64>,
37 pub time_window_days: Option<u32>,
39 pub enabled: Option<bool>,
41}
42
43pub async fn create_workspace_budget<S: DriftBudgetStateProvider>(
47 State(state): State<S>,
48 Json(request): Json<CreateWorkspaceBudgetRequest>,
49) -> Result<Json<serde_json::Value>, StatusCode> {
50 let budget = DriftBudget {
51 max_breaking_changes: request.max_breaking_changes.unwrap_or(0),
52 max_non_breaking_changes: request.max_non_breaking_changes.unwrap_or(10),
53 max_field_churn_percent: request.max_field_churn_percent,
54 time_window_days: request.time_window_days,
55 severity_threshold: crate::ai_contract_diff::MismatchSeverity::High,
56 enabled: request.enabled.unwrap_or(true),
57 };
58
59 let mut config = state.engine().config().clone();
60 config
61 .per_workspace_budgets
62 .insert(request.workspace_id.clone(), budget.clone());
63
64 Ok(Json(serde_json::json!({
68 "workspace_id": request.workspace_id,
69 "budget": budget,
70 })))
71}
72
73pub async fn create_service_budget<S: DriftBudgetStateProvider>(
77 State(state): State<S>,
78 Json(request): Json<CreateServiceBudgetRequest>,
79) -> Result<Json<serde_json::Value>, StatusCode> {
80 let budget = DriftBudget {
81 max_breaking_changes: request.max_breaking_changes.unwrap_or(0),
82 max_non_breaking_changes: request.max_non_breaking_changes.unwrap_or(10),
83 max_field_churn_percent: request.max_field_churn_percent,
84 time_window_days: request.time_window_days,
85 severity_threshold: crate::ai_contract_diff::MismatchSeverity::High,
86 enabled: request.enabled.unwrap_or(true),
87 };
88
89 let mut config = state.engine().config().clone();
90 config.per_service_budgets.insert(request.service_name.clone(), budget.clone());
91
92 Ok(Json(serde_json::json!({
96 "service_name": request.service_name,
97 "budget": budget,
98 })))
99}