omni_orchestrator/schemas/v1/api/cost/
metrics.rs

1use super::super::super::db::queries as db;
2use super::types::CreateCostMetricRequest;
3use rocket::http::Status;
4use rocket::serde::json::{json, Json, Value};
5use rocket::{delete, get, post, State};
6use std::sync::Arc;
7use crate::DatabaseManager;
8use chrono::{DateTime, Utc};
9
10use libomni::types::db::v1 as types;
11use types::cost::{CostMetric, CostMetricWithType};
12
13/// List cost metrics with pagination and filtering support.
14#[get("/platform/<platform_id>/cost_metrics?<page>&<per_page>&<resource_type_id>&<provider_id>&<app_id>&<start_date>&<end_date>&<billing_period>")]
15pub async fn list_cost_metrics(
16    platform_id: i64,
17    page: Option<i64>,
18    per_page: Option<i64>,
19    resource_type_id: Option<i32>,
20    provider_id: Option<i64>,
21    app_id: Option<i64>,
22    start_date: Option<String>,
23    end_date: Option<String>,
24    billing_period: Option<String>,
25    db_manager: &State<Arc<DatabaseManager>>,
26) -> Result<Json<Value>, (Status, Json<Value>)> {
27    // Get platform information
28    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
29        Ok(platform) => platform,
30        Err(_) => {
31            return Err((
32                Status::NotFound,
33                Json(json!({
34                    "error": "Platform not found",
35                    "message": format!("Platform with ID {} does not exist", platform_id)
36                }))
37            ));
38        }
39    };
40
41    // Get platform-specific database pool
42    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
43        Ok(pool) => pool,
44        Err(_) => {
45            return Err((
46                Status::InternalServerError,
47                Json(json!({
48                    "error": "Database error",
49                    "message": "Failed to connect to platform database"
50                }))
51            ));
52        }
53    };
54
55    use chrono::TimeZone;
56
57    // Parse start_date and end_date from Option<String> to Option<DateTime<Utc>>
58    let parsed_start_date = match start_date {
59        Some(ref s) => match DateTime::parse_from_rfc3339(s) {
60            Ok(dt) => Some(dt.with_timezone(&Utc)),
61            Err(_) => None,
62        },
63        None => None,
64    };
65    let parsed_end_date = match end_date {
66        Some(ref s) => match DateTime::parse_from_rfc3339(s) {
67            Ok(dt) => Some(dt.with_timezone(&Utc)),
68            Err(_) => None,
69        },
70        None => None,
71    };
72
73    match (page, per_page) {
74        (Some(p), Some(pp)) => {
75            let cost_metrics = match db::cost::list_cost_metrics(
76                &pool, p, pp, resource_type_id, provider_id, app_id, parsed_start_date, parsed_end_date, billing_period.as_deref()
77            ).await {
78                Ok(metrics) => metrics,
79                Err(_) => {
80                    return Err((
81                        Status::InternalServerError,
82                        Json(json!({
83                            "error": "Database error",
84                            "message": "Failed to retrieve cost metrics"
85                        }))
86                    ));
87                }
88            };
89            
90            let total_count = match db::cost::count_cost_metrics(
91                &pool, resource_type_id, provider_id, app_id, parsed_start_date, parsed_end_date, billing_period.as_deref()
92            ).await {
93                Ok(count) => count,
94                Err(_) => {
95                    return Err((
96                        Status::InternalServerError,
97                        Json(json!({
98                            "error": "Database error",
99                            "message": "Failed to count cost metrics"
100                        }))
101                    ));
102                }
103            };
104            
105            let total_pages = (total_count as f64 / pp as f64).ceil() as i64;
106
107            let response = json!({
108                "cost_metrics": cost_metrics,
109                "pagination": {
110                    "page": p,
111                    "per_page": pp,
112                    "total_count": total_count,
113                    "total_pages": total_pages
114                }
115            });
116
117            Ok(Json(response))
118        }
119        _ => Err((
120            Status::BadRequest,
121            Json(json!({
122                "error": "Missing pagination parameters",
123                "message": "Please provide both 'page' and 'per_page' parameters"
124            }))
125        ))
126    }
127}
128
129/// Get a specific cost metric by ID.
130#[get("/platform/<platform_id>/cost_metrics/<id>")]
131pub async fn get_cost_metric(
132    platform_id: i64,
133    id: i64,
134    db_manager: &State<Arc<DatabaseManager>>,
135) -> Result<Json<CostMetricWithType>, (Status, Json<Value>)> {
136    // Get platform information
137    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
138        Ok(platform) => platform,
139        Err(_) => {
140            return Err((
141                Status::NotFound,
142                Json(json!({
143                    "error": "Platform not found",
144                    "message": format!("Platform with ID {} does not exist", platform_id)
145                }))
146            ));
147        }
148    };
149
150    // Get platform-specific database pool
151    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
152        Ok(pool) => pool,
153        Err(_) => {
154            return Err((
155                Status::InternalServerError,
156                Json(json!({
157                    "error": "Database error",
158                    "message": "Failed to connect to platform database"
159                }))
160            ));
161        }
162    };
163
164    match db::cost::get_cost_metric_by_id(&pool, id).await {
165        Ok(cost_metric) => Ok(Json(cost_metric)),
166        Err(e) => Err((
167            Status::NotFound,
168            Json(json!({
169                "error": "Cost metric not found",
170                "message": format!("Cost metric with ID {} could not be found: {}", id, e)
171            }))
172        )),
173    }
174}
175
176/// Create a new cost metric.
177#[post("/platform/<platform_id>/cost_metrics", format = "json", data = "<request>")]
178pub async fn create_cost_metric(
179    platform_id: i64,
180    request: Json<CreateCostMetricRequest>,
181    db_manager: &State<Arc<DatabaseManager>>,
182) -> Result<Json<CostMetric>, (Status, Json<Value>)> {
183    // Get platform information
184    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
185        Ok(platform) => platform,
186        Err(_) => {
187            return Err((
188                Status::NotFound,
189                Json(json!({
190                    "error": "Platform not found",
191                    "message": format!("Platform with ID {} does not exist", platform_id)
192                }))
193            ));
194        }
195    };
196
197    // Get platform-specific database pool
198    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
199        Ok(pool) => pool,
200        Err(_) => {
201            return Err((
202                Status::InternalServerError,
203                Json(json!({
204                    "error": "Database error",
205                    "message": "Failed to connect to platform database"
206                }))
207            ));
208        }
209    };
210
211    match db::cost::create_cost_metric(
212        &pool,
213        request.resource_type_id,
214        request.provider_id,
215        request.region_id,
216        request.app_id,
217        request.worker_id,
218        request.org_id,
219        request.start_time,
220        request.end_time,
221        request.usage_quantity,
222        request.unit_cost,
223        &request.currency,
224        request.total_cost,
225        request.discount_percentage,
226        request.discount_reason.as_deref(),
227        request.billing_period.as_deref(),
228    ).await {
229        Ok(cost_metric) => Ok(Json(cost_metric)),
230        Err(e) => Err((
231            Status::InternalServerError,
232            Json(json!({
233                "error": "Failed to create cost metric",
234                "message": format!("{}", e)
235            }))
236        )),
237    }
238}
239
240/// Delete a cost metric.
241#[delete("/platform/<platform_id>/cost_metrics/<id>")]
242pub async fn delete_cost_metric(
243    platform_id: i64,
244    id: i64,
245    db_manager: &State<Arc<DatabaseManager>>,
246) -> Result<Json<Value>, (Status, Json<Value>)> {
247    // Get platform information
248    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
249        Ok(platform) => platform,
250        Err(_) => {
251            return Err((
252                Status::NotFound,
253                Json(json!({
254                    "error": "Platform not found",
255                    "message": format!("Platform with ID {} does not exist", platform_id)
256                }))
257            ));
258        }
259    };
260
261    // Get platform-specific database pool
262    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
263        Ok(pool) => pool,
264        Err(_) => {
265            return Err((
266                Status::InternalServerError,
267                Json(json!({
268                    "error": "Database error",
269                    "message": "Failed to connect to platform database"
270                }))
271            ));
272        }
273    };
274
275    match db::cost::delete_cost_metric(&pool, id).await {
276        Ok(_) => Ok(Json(json!({ "status": "deleted" }))),
277        Err(e) => Err((
278            Status::InternalServerError,
279            Json(json!({
280                "error": "Failed to delete cost metric",
281                "message": format!("{}", e)
282            }))
283        )),
284    }
285}