1use std::sync::Arc;
12use super::super::super::auth::User;
13use crate::DatabaseManager;
14use crate::models::{
15 util_tables::ResourceType,
16 cost::{
17 CostBudget,
18 CostMetric,
19 CostProjection,
20 ResourcePricing,
21 CostAllocationTag,
22 CostMetricWithType,
23 }
24};
25use super::super::db::queries as db;
26use rocket::http::Status;
27use rocket::serde::json::{json, Json, Value};
28use rocket::{delete, get, post, put, State};
29use serde::{Deserialize, Serialize};
30use chrono::{DateTime, Utc};
31
32#[derive(Debug, Serialize, Deserialize)]
34pub struct CreateResourceTypeRequest {
35 name: String,
37 category: String,
39 unit_of_measurement: String,
41 description: Option<String>,
43}
44
45#[derive(Debug, Serialize, Deserialize)]
47pub struct UpdateResourceTypeRequest {
48 name: Option<String>,
50 category: Option<String>,
52 unit_of_measurement: Option<String>,
54 description: Option<String>,
56}
57
58#[derive(Debug, Serialize, Deserialize)]
60pub struct CreateCostMetricRequest {
61 resource_type_id: i32,
63 provider_id: Option<i64>,
65 region_id: Option<i64>,
67 app_id: Option<i64>,
69 worker_id: Option<i64>,
71 org_id: Option<i64>,
73 start_time: DateTime<Utc>,
75 end_time: DateTime<Utc>,
77 usage_quantity: f64,
79 unit_cost: f64,
81 currency: String,
83 total_cost: f64,
85 discount_percentage: Option<f64>,
87 discount_reason: Option<String>,
89 billing_period: Option<String>,
91}
92
93#[derive(Debug, Serialize, Deserialize)]
95pub struct CostMetricFilter {
96 resource_type_id: Option<i32>,
98 provider_id: Option<i64>,
100 app_id: Option<i64>,
102 start_date: Option<DateTime<Utc>>,
104 end_date: Option<DateTime<Utc>>,
106 billing_period: Option<String>,
108}
109
110#[derive(Debug, Serialize, Deserialize)]
112pub struct CreateCostBudgetRequest {
113 org_id: i64,
115 app_id: Option<i64>,
117 budget_name: String,
119 budget_amount: f64,
121 currency: String,
123 budget_period: String,
125 period_start: DateTime<Utc>,
127 period_end: DateTime<Utc>,
129 alert_threshold_percentage: f64,
131 alert_contacts: String,
133}
134
135#[derive(Debug, Serialize, Deserialize)]
137pub struct UpdateCostBudgetRequest {
138 budget_name: Option<String>,
140 budget_amount: Option<f64>,
142 alert_threshold_percentage: Option<f64>,
144 alert_contacts: Option<String>,
146 is_active: Option<bool>,
148}
149
150#[derive(Debug, Serialize, Deserialize)]
152pub struct CreateCostProjectionRequest {
153 org_id: i64,
155 app_id: Option<i64>,
157 projection_period: String,
159 start_date: DateTime<Utc>,
161 end_date: DateTime<Utc>,
163 projected_cost: f64,
165 currency: String,
167 projection_model: String,
169 confidence_level: Option<f64>,
171 metadata: Option<String>,
173}
174
175#[derive(Debug, Serialize, Deserialize)]
177pub struct CreateResourcePricingRequest {
178 resource_type_id: i32,
180 provider_id: i64,
182 region_id: Option<i64>,
184 tier_name: String,
186 unit_price: f64,
188 currency: String,
190 effective_from: DateTime<Utc>,
192 effective_to: Option<DateTime<Utc>>,
194 pricing_model: String,
196 commitment_period: Option<String>,
198 volume_discount_tiers: Option<String>,
200}
201
202#[derive(Debug, Serialize, Deserialize)]
204pub struct UpdateResourcePricingRequest {
205 unit_price: Option<f64>,
207 effective_to: Option<DateTime<Utc>>,
209 volume_discount_tiers: Option<String>,
211}
212
213#[derive(Debug, Serialize, Deserialize)]
215pub struct CreateCostAllocationTagRequest {
216 tag_key: String,
218 tag_value: String,
220 resource_id: i64,
222 resource_type: String,
224}
225
226#[derive(Debug, Serialize, Deserialize)]
228pub struct CostAnalysisByDimensionRequest {
229 dimension: String,
231 start_date: DateTime<Utc>,
233 end_date: DateTime<Utc>,
235 limit: i64,
237}
238
239#[derive(Debug, Serialize, Deserialize)]
241pub struct CostOverTimeRequest {
242 app_id: i64,
244 interval: String,
246 start_date: DateTime<Utc>,
248 end_date: DateTime<Utc>,
250}
251
252#[get("/platform/<platform_id>/resource_types?<page>&<per_page>")]
254pub async fn list_resource_types(
255 platform_id: i64,
256 page: Option<i64>,
257 per_page: Option<i64>,
258 db_manager: &State<Arc<DatabaseManager>>,
259) -> Result<Json<Value>, (Status, Json<Value>)> {
260 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
262 Ok(platform) => platform,
263 Err(_) => {
264 return Err((
265 Status::NotFound,
266 Json(json!({
267 "error": "Platform not found",
268 "message": format!("Platform with ID {} does not exist", platform_id)
269 }))
270 ));
271 }
272 };
273
274 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
276 Ok(pool) => pool,
277 Err(_) => {
278 return Err((
279 Status::InternalServerError,
280 Json(json!({
281 "error": "Database error",
282 "message": "Failed to connect to platform database"
283 }))
284 ));
285 }
286 };
287
288 match (page, per_page) {
289 (Some(p), Some(pp)) => {
290 let resource_types = match db::cost::list_resource_types(&pool, p, pp).await {
291 Ok(types) => types,
292 Err(_) => {
293 return Err((
294 Status::InternalServerError,
295 Json(json!({
296 "error": "Database error",
297 "message": "Failed to retrieve resource types"
298 }))
299 ));
300 }
301 };
302
303 let total_count = match db::cost::count_resource_types(&pool).await {
304 Ok(count) => count,
305 Err(_) => {
306 return Err((
307 Status::InternalServerError,
308 Json(json!({
309 "error": "Database error",
310 "message": "Failed to count resource types"
311 }))
312 ));
313 }
314 };
315
316 let total_pages = (total_count as f64 / pp as f64).ceil() as i64;
317
318 let response = json!({
319 "resource_types": resource_types,
320 "pagination": {
321 "page": p,
322 "per_page": pp,
323 "total_count": total_count,
324 "total_pages": total_pages
325 }
326 });
327
328 Ok(Json(response))
329 }
330 _ => Err((
331 Status::BadRequest,
332 Json(json!({
333 "error": "Missing pagination parameters",
334 "message": "Please provide both 'page' and 'per_page' parameters"
335 }))
336 ))
337 }
338}
339
340#[get("/platform/<platform_id>/count/resource_types")]
342pub async fn count_resource_types(
343 platform_id: i64,
344 db_manager: &State<Arc<DatabaseManager>>,
345) -> Result<Json<i64>, (Status, Json<Value>)> {
346 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
348 Ok(platform) => platform,
349 Err(_) => {
350 return Err((
351 Status::NotFound,
352 Json(json!({
353 "error": "Platform not found",
354 "message": format!("Platform with ID {} does not exist", platform_id)
355 }))
356 ));
357 }
358 };
359
360 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
362 Ok(pool) => pool,
363 Err(_) => {
364 return Err((
365 Status::InternalServerError,
366 Json(json!({
367 "error": "Database error",
368 "message": "Failed to connect to platform database"
369 }))
370 ));
371 }
372 };
373
374 match db::cost::count_resource_types(&pool).await {
375 Ok(count) => Ok(Json(count)),
376 Err(_) => Err((
377 Status::InternalServerError,
378 Json(json!({
379 "error": "Database error",
380 "message": "Failed to count resource types"
381 }))
382 )),
383 }
384}
385
386#[get("/platform/<platform_id>/resource_types/<id>")]
388pub async fn get_resource_type(
389 platform_id: i64,
390 id: i32,
391 db_manager: &State<Arc<DatabaseManager>>,
392) -> Result<Json<ResourceType>, (Status, Json<Value>)> {
393 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
395 Ok(platform) => platform,
396 Err(_) => {
397 return Err((
398 Status::NotFound,
399 Json(json!({
400 "error": "Platform not found",
401 "message": format!("Platform with ID {} does not exist", platform_id)
402 }))
403 ));
404 }
405 };
406
407 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
409 Ok(pool) => pool,
410 Err(_) => {
411 return Err((
412 Status::InternalServerError,
413 Json(json!({
414 "error": "Database error",
415 "message": "Failed to connect to platform database"
416 }))
417 ));
418 }
419 };
420
421 match db::cost::get_resource_type_by_id(&pool, id).await {
422 Ok(resource_type) => Ok(Json(resource_type)),
423 Err(_) => Err((
424 Status::NotFound,
425 Json(json!({
426 "error": "Resource type not found",
427 "message": format!("Resource type with ID {} could not be found", id)
428 }))
429 )),
430 }
431}
432
433#[post("/platform/<platform_id>/resource_types", format = "json", data = "<request>")]
435pub async fn create_resource_type(
436 platform_id: i64,
437 request: Json<CreateResourceTypeRequest>,
438 db_manager: &State<Arc<DatabaseManager>>,
439) -> Result<Json<ResourceType>, (Status, Json<Value>)> {
440 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
442 Ok(platform) => platform,
443 Err(_) => {
444 return Err((
445 Status::NotFound,
446 Json(json!({
447 "error": "Platform not found",
448 "message": format!("Platform with ID {} does not exist", platform_id)
449 }))
450 ));
451 }
452 };
453
454 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
456 Ok(pool) => pool,
457 Err(_) => {
458 return Err((
459 Status::InternalServerError,
460 Json(json!({
461 "error": "Database error",
462 "message": "Failed to connect to platform database"
463 }))
464 ));
465 }
466 };
467
468 match db::cost::create_resource_type(
469 &pool,
470 &request.name,
471 &request.category,
472 &request.unit_of_measurement,
473 request.description.as_deref(),
474 ).await {
475 Ok(resource_type) => Ok(Json(resource_type)),
476 Err(e) => Err((
477 Status::InternalServerError,
478 Json(json!({
479 "error": "Failed to create resource type",
480 "message": format!("{}", e)
481 }))
482 )),
483 }
484}
485
486#[put("/platform/<platform_id>/resource_types/<id>", format = "json", data = "<request>")]
488pub async fn update_resource_type(
489 platform_id: i64,
490 id: i32,
491 request: Json<UpdateResourceTypeRequest>,
492 db_manager: &State<Arc<DatabaseManager>>,
493) -> Result<Json<ResourceType>, (Status, Json<Value>)> {
494 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
496 Ok(platform) => platform,
497 Err(_) => {
498 return Err((
499 Status::NotFound,
500 Json(json!({
501 "error": "Platform not found",
502 "message": format!("Platform with ID {} does not exist", platform_id)
503 }))
504 ));
505 }
506 };
507
508 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
510 Ok(pool) => pool,
511 Err(_) => {
512 return Err((
513 Status::InternalServerError,
514 Json(json!({
515 "error": "Database error",
516 "message": "Failed to connect to platform database"
517 }))
518 ));
519 }
520 };
521
522 match db::cost::update_resource_type(
523 &pool,
524 id,
525 request.name.as_deref(),
526 request.category.as_deref(),
527 request.unit_of_measurement.as_deref(),
528 request.description.as_deref(),
529 ).await {
530 Ok(resource_type) => Ok(Json(resource_type)),
531 Err(e) => Err((
532 Status::InternalServerError,
533 Json(json!({
534 "error": "Failed to update resource type",
535 "message": format!("{}", e)
536 }))
537 )),
538 }
539}
540
541#[delete("/platform/<platform_id>/resource_types/<id>")]
543pub async fn delete_resource_type(
544 platform_id: i64,
545 id: i32,
546 db_manager: &State<Arc<DatabaseManager>>,
547) -> Result<Json<Value>, (Status, Json<Value>)> {
548 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
550 Ok(platform) => platform,
551 Err(_) => {
552 return Err((
553 Status::NotFound,
554 Json(json!({
555 "error": "Platform not found",
556 "message": format!("Platform with ID {} does not exist", platform_id)
557 }))
558 ));
559 }
560 };
561
562 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
564 Ok(pool) => pool,
565 Err(_) => {
566 return Err((
567 Status::InternalServerError,
568 Json(json!({
569 "error": "Database error",
570 "message": "Failed to connect to platform database"
571 }))
572 ));
573 }
574 };
575
576 match db::cost::delete_resource_type(&pool, id).await {
577 Ok(_) => Ok(Json(json!({ "status": "deleted" }))),
578 Err(e) => Err((
579 Status::InternalServerError,
580 Json(json!({
581 "error": "Failed to delete resource type",
582 "message": format!("{}", e)
583 }))
584 )),
585 }
586}
587
588#[get("/platform/<platform_id>/cost_metrics?<page>&<per_page>&<resource_type_id>&<provider_id>&<app_id>&<start_date>&<end_date>&<billing_period>")]
592pub async fn list_cost_metrics(
593 platform_id: i64,
594 page: Option<i64>,
595 per_page: Option<i64>,
596 resource_type_id: Option<i32>,
597 provider_id: Option<i64>,
598 app_id: Option<i64>,
599 start_date: Option<String>,
600 end_date: Option<String>,
601 billing_period: Option<String>,
602 db_manager: &State<Arc<DatabaseManager>>,
603) -> Result<Json<Value>, (Status, Json<Value>)> {
604 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
606 Ok(platform) => platform,
607 Err(_) => {
608 return Err((
609 Status::NotFound,
610 Json(json!({
611 "error": "Platform not found",
612 "message": format!("Platform with ID {} does not exist", platform_id)
613 }))
614 ));
615 }
616 };
617
618 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
620 Ok(pool) => pool,
621 Err(_) => {
622 return Err((
623 Status::InternalServerError,
624 Json(json!({
625 "error": "Database error",
626 "message": "Failed to connect to platform database"
627 }))
628 ));
629 }
630 };
631
632 use chrono::TimeZone;
633
634 let parsed_start_date = match start_date {
636 Some(ref s) => match DateTime::parse_from_rfc3339(s) {
637 Ok(dt) => Some(dt.with_timezone(&Utc)),
638 Err(_) => None,
639 },
640 None => None,
641 };
642 let parsed_end_date = match end_date {
643 Some(ref s) => match DateTime::parse_from_rfc3339(s) {
644 Ok(dt) => Some(dt.with_timezone(&Utc)),
645 Err(_) => None,
646 },
647 None => None,
648 };
649
650 match (page, per_page) {
651 (Some(p), Some(pp)) => {
652 let cost_metrics = match db::cost::list_cost_metrics(
653 &pool, p, pp, resource_type_id, provider_id, app_id, parsed_start_date, parsed_end_date, billing_period.as_deref()
654 ).await {
655 Ok(metrics) => metrics,
656 Err(_) => {
657 return Err((
658 Status::InternalServerError,
659 Json(json!({
660 "error": "Database error",
661 "message": "Failed to retrieve cost metrics"
662 }))
663 ));
664 }
665 };
666
667 let total_count = match db::cost::count_cost_metrics(
668 &pool, resource_type_id, provider_id, app_id, parsed_start_date, parsed_end_date, billing_period.as_deref()
669 ).await {
670 Ok(count) => count,
671 Err(_) => {
672 return Err((
673 Status::InternalServerError,
674 Json(json!({
675 "error": "Database error",
676 "message": "Failed to count cost metrics"
677 }))
678 ));
679 }
680 };
681
682 let total_pages = (total_count as f64 / pp as f64).ceil() as i64;
683
684 let response = json!({
685 "cost_metrics": cost_metrics,
686 "pagination": {
687 "page": p,
688 "per_page": pp,
689 "total_count": total_count,
690 "total_pages": total_pages
691 }
692 });
693
694 Ok(Json(response))
695 }
696 _ => Err((
697 Status::BadRequest,
698 Json(json!({
699 "error": "Missing pagination parameters",
700 "message": "Please provide both 'page' and 'per_page' parameters"
701 }))
702 ))
703 }
704}
705
706#[get("/platform/<platform_id>/cost_metrics/<id>")]
708pub async fn get_cost_metric(
709 platform_id: i64,
710 id: i64,
711 db_manager: &State<Arc<DatabaseManager>>,
712) -> Result<Json<CostMetricWithType>, (Status, Json<Value>)> {
713 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
715 Ok(platform) => platform,
716 Err(_) => {
717 return Err((
718 Status::NotFound,
719 Json(json!({
720 "error": "Platform not found",
721 "message": format!("Platform with ID {} does not exist", platform_id)
722 }))
723 ));
724 }
725 };
726
727 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
729 Ok(pool) => pool,
730 Err(_) => {
731 return Err((
732 Status::InternalServerError,
733 Json(json!({
734 "error": "Database error",
735 "message": "Failed to connect to platform database"
736 }))
737 ));
738 }
739 };
740
741 match db::cost::get_cost_metric_by_id(&pool, id).await {
742 Ok(cost_metric) => Ok(Json(cost_metric)),
743 Err(e) => Err((
744 Status::NotFound,
745 Json(json!({
746 "error": "Cost metric not found",
747 "message": format!("Cost metric with ID {} could not be found: {}", id, e)
748 }))
749 )),
750 }
751}
752
753#[post("/platform/<platform_id>/cost_metrics", format = "json", data = "<request>")]
755pub async fn create_cost_metric(
756 platform_id: i64,
757 request: Json<CreateCostMetricRequest>,
758 db_manager: &State<Arc<DatabaseManager>>,
759) -> Result<Json<CostMetric>, (Status, Json<Value>)> {
760 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
762 Ok(platform) => platform,
763 Err(_) => {
764 return Err((
765 Status::NotFound,
766 Json(json!({
767 "error": "Platform not found",
768 "message": format!("Platform with ID {} does not exist", platform_id)
769 }))
770 ));
771 }
772 };
773
774 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
776 Ok(pool) => pool,
777 Err(_) => {
778 return Err((
779 Status::InternalServerError,
780 Json(json!({
781 "error": "Database error",
782 "message": "Failed to connect to platform database"
783 }))
784 ));
785 }
786 };
787
788 match db::cost::create_cost_metric(
789 &pool,
790 request.resource_type_id,
791 request.provider_id,
792 request.region_id,
793 request.app_id,
794 request.worker_id,
795 request.org_id,
796 request.start_time,
797 request.end_time,
798 request.usage_quantity,
799 request.unit_cost,
800 &request.currency,
801 request.total_cost,
802 request.discount_percentage,
803 request.discount_reason.as_deref(),
804 request.billing_period.as_deref(),
805 ).await {
806 Ok(cost_metric) => Ok(Json(cost_metric)),
807 Err(e) => Err((
808 Status::InternalServerError,
809 Json(json!({
810 "error": "Failed to create cost metric",
811 "message": format!("{}", e)
812 }))
813 )),
814 }
815}
816
817#[delete("/platform/<platform_id>/cost_metrics/<id>")]
819pub async fn delete_cost_metric(
820 platform_id: i64,
821 id: i64,
822 db_manager: &State<Arc<DatabaseManager>>,
823) -> Result<Json<Value>, (Status, Json<Value>)> {
824 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
826 Ok(platform) => platform,
827 Err(_) => {
828 return Err((
829 Status::NotFound,
830 Json(json!({
831 "error": "Platform not found",
832 "message": format!("Platform with ID {} does not exist", platform_id)
833 }))
834 ));
835 }
836 };
837
838 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
840 Ok(pool) => pool,
841 Err(_) => {
842 return Err((
843 Status::InternalServerError,
844 Json(json!({
845 "error": "Database error",
846 "message": "Failed to connect to platform database"
847 }))
848 ));
849 }
850 };
851
852 match db::cost::delete_cost_metric(&pool, id).await {
853 Ok(_) => Ok(Json(json!({ "status": "deleted" }))),
854 Err(e) => Err((
855 Status::InternalServerError,
856 Json(json!({
857 "error": "Failed to delete cost metric",
858 "message": format!("{}", e)
859 }))
860 )),
861 }
862}
863
864#[post("/platform/<platform_id>/cost_analysis/by_dimension", format = "json", data = "<request>")]
866pub async fn analyze_costs_by_dimension(
867 platform_id: i64,
868 request: Json<CostAnalysisByDimensionRequest>,
869 db_manager: &State<Arc<DatabaseManager>>,
870) -> Result<Json<Vec<(String, f64)>>, (Status, Json<Value>)> {
871 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
873 Ok(platform) => platform,
874 Err(_) => {
875 return Err((
876 Status::NotFound,
877 Json(json!({
878 "error": "Platform not found",
879 "message": format!("Platform with ID {} does not exist", platform_id)
880 }))
881 ));
882 }
883 };
884
885 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
887 Ok(pool) => pool,
888 Err(_) => {
889 return Err((
890 Status::InternalServerError,
891 Json(json!({
892 "error": "Database error",
893 "message": "Failed to connect to platform database"
894 }))
895 ));
896 }
897 };
898
899 match db::cost::get_cost_metrics_by_dimension(
900 &pool,
901 &request.dimension,
902 request.start_date,
903 request.end_date,
904 request.limit,
905 ).await {
906 Ok(results) => Ok(Json(results)),
907 Err(e) => Err((
908 Status::InternalServerError,
909 Json(json!({
910 "error": "Failed to analyze costs by dimension",
911 "message": format!("{}", e)
912 }))
913 )),
914 }
915}
916
917#[post("/platform/<platform_id>/cost_analysis/over_time", format = "json", data = "<request>")]
919pub async fn analyze_cost_over_time(
920 platform_id: i64,
921 request: Json<CostOverTimeRequest>,
922 db_manager: &State<Arc<DatabaseManager>>,
923) -> Result<Json<Vec<(DateTime<Utc>, f64)>>, (Status, Json<Value>)> {
924 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
926 Ok(platform) => platform,
927 Err(_) => {
928 return Err((
929 Status::NotFound,
930 Json(json!({
931 "error": "Platform not found",
932 "message": format!("Platform with ID {} does not exist", platform_id)
933 }))
934 ));
935 }
936 };
937
938 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
940 Ok(pool) => pool,
941 Err(_) => {
942 return Err((
943 Status::InternalServerError,
944 Json(json!({
945 "error": "Database error",
946 "message": "Failed to connect to platform database"
947 }))
948 ));
949 }
950 };
951
952 match db::cost::get_app_cost_over_time(
953 &pool,
954 request.app_id,
955 &request.interval,
956 request.start_date,
957 request.end_date,
958 ).await {
959 Ok(results) => Ok(Json(results)),
960 Err(e) => Err((
961 Status::InternalServerError,
962 Json(json!({
963 "error": "Failed to analyze cost over time",
964 "message": format!("{}", e)
965 }))
966 )),
967 }
968}
969
970#[get("/platform/<platform_id>/cost_budgets?<page>&<per_page>")]
974pub async fn list_cost_budgets(
975 platform_id: i64,
976 page: Option<i64>,
977 per_page: Option<i64>,
978 db_manager: &State<Arc<DatabaseManager>>,
979) -> Result<Json<Value>, (Status, Json<Value>)> {
980 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
982 Ok(platform) => platform,
983 Err(_) => {
984 return Err((
985 Status::NotFound,
986 Json(json!({
987 "error": "Platform not found",
988 "message": format!("Platform with ID {} does not exist", platform_id)
989 }))
990 ));
991 }
992 };
993
994 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
996 Ok(pool) => pool,
997 Err(_) => {
998 return Err((
999 Status::InternalServerError,
1000 Json(json!({
1001 "error": "Database error",
1002 "message": "Failed to connect to platform database"
1003 }))
1004 ));
1005 }
1006 };
1007
1008 match (page, per_page) {
1009 (Some(p), Some(pp)) => {
1010 let cost_budgets = match db::cost::list_cost_budgets(&pool, p, pp).await {
1011 Ok(budgets) => budgets,
1012 Err(_) => {
1013 return Err((
1014 Status::InternalServerError,
1015 Json(json!({
1016 "error": "Database error",
1017 "message": "Failed to retrieve cost budgets"
1018 }))
1019 ));
1020 }
1021 };
1022
1023 let total_count = match db::cost::count_cost_budgets(&pool).await {
1024 Ok(count) => count,
1025 Err(_) => {
1026 return Err((
1027 Status::InternalServerError,
1028 Json(json!({
1029 "error": "Database error",
1030 "message": "Failed to count cost budgets"
1031 }))
1032 ));
1033 }
1034 };
1035
1036 let total_pages = (total_count as f64 / pp as f64).ceil() as i64;
1037
1038 let response = json!({
1039 "cost_budgets": cost_budgets,
1040 "pagination": {
1041 "page": p,
1042 "per_page": pp,
1043 "total_count": total_count,
1044 "total_pages": total_pages
1045 }
1046 });
1047
1048 Ok(Json(response))
1049 }
1050 _ => Err((
1051 Status::BadRequest,
1052 Json(json!({
1053 "error": "Missing pagination parameters",
1054 "message": "Please provide both 'page' and 'per_page' parameters"
1055 }))
1056 ))
1057 }
1058}
1059
1060#[get("/platform/<platform_id>/cost_budgets/<id>")]
1062pub async fn get_cost_budget(
1063 platform_id: i64,
1064 id: i64,
1065 db_manager: &State<Arc<DatabaseManager>>,
1066) -> Result<Json<CostBudget>, (Status, Json<Value>)> {
1067 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1069 Ok(platform) => platform,
1070 Err(_) => {
1071 return Err((
1072 Status::NotFound,
1073 Json(json!({
1074 "error": "Platform not found",
1075 "message": format!("Platform with ID {} does not exist", platform_id)
1076 }))
1077 ));
1078 }
1079 };
1080
1081 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1083 Ok(pool) => pool,
1084 Err(_) => {
1085 return Err((
1086 Status::InternalServerError,
1087 Json(json!({
1088 "error": "Database error",
1089 "message": "Failed to connect to platform database"
1090 }))
1091 ));
1092 }
1093 };
1094
1095 match db::cost::get_cost_budget_by_id(&pool, id).await {
1096 Ok(budget) => Ok(Json(budget)),
1097 Err(_) => Err((
1098 Status::NotFound,
1099 Json(json!({
1100 "error": "Cost budget not found",
1101 "message": format!("Cost budget with ID {} could not be found", id)
1102 }))
1103 )),
1104 }
1105}
1106
1107#[post("/platform/<platform_id>/cost_budgets", format = "json", data = "<request>")]
1109pub async fn create_cost_budget(
1110 platform_id: i64,
1111 request: Json<CreateCostBudgetRequest>,
1112 db_manager: &State<Arc<DatabaseManager>>,
1113 user: User,
1114) -> Result<Json<CostBudget>, (Status, Json<Value>)> {
1115 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1117 Ok(platform) => platform,
1118 Err(_) => {
1119 return Err((
1120 Status::NotFound,
1121 Json(json!({
1122 "error": "Platform not found",
1123 "message": format!("Platform with ID {} does not exist", platform_id)
1124 }))
1125 ));
1126 }
1127 };
1128
1129 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1131 Ok(pool) => pool,
1132 Err(_) => {
1133 return Err((
1134 Status::InternalServerError,
1135 Json(json!({
1136 "error": "Database error",
1137 "message": "Failed to connect to platform database"
1138 }))
1139 ));
1140 }
1141 };
1142
1143 let user_id = user.id;
1144
1145 match db::cost::create_cost_budget(
1148 &pool,
1149 request.org_id,
1150 request.app_id,
1151 &request.budget_name,
1152 request.budget_amount,
1153 &request.currency,
1154 &request.budget_period,
1155 request.period_start,
1156 request.period_end,
1157 request.alert_threshold_percentage,
1158 &request.alert_contacts,
1159 user_id,
1160 ).await {
1161 Ok(budget) => Ok(Json(budget)),
1162 Err(e) => Err((
1163 Status::InternalServerError,
1164 Json(json!({
1165 "error": "Failed to create cost budget",
1166 "message": format!("{}", e)
1167 }))
1168 )),
1169 }
1170}
1171
1172#[put("/platform/<platform_id>/cost_budgets/<id>", format = "json", data = "<request>")]
1174pub async fn update_cost_budget(
1175 platform_id: i64,
1176 id: i64,
1177 request: Json<UpdateCostBudgetRequest>,
1178 db_manager: &State<Arc<DatabaseManager>>,
1179) -> Result<Json<CostBudget>, (Status, Json<Value>)> {
1180 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1182 Ok(platform) => platform,
1183 Err(_) => {
1184 return Err((
1185 Status::NotFound,
1186 Json(json!({
1187 "error": "Platform not found",
1188 "message": format!("Platform with ID {} does not exist", platform_id)
1189 }))
1190 ));
1191 }
1192 };
1193
1194 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1196 Ok(pool) => pool,
1197 Err(_) => {
1198 return Err((
1199 Status::InternalServerError,
1200 Json(json!({
1201 "error": "Database error",
1202 "message": "Failed to connect to platform database"
1203 }))
1204 ));
1205 }
1206 };
1207
1208 match db::cost::update_cost_budget(
1209 &pool,
1210 id,
1211 request.budget_name.as_deref(),
1212 request.budget_amount,
1213 request.alert_threshold_percentage,
1214 request.alert_contacts.as_deref(),
1215 request.is_active,
1216 ).await {
1217 Ok(budget) => Ok(Json(budget)),
1218 Err(e) => Err((
1219 Status::InternalServerError,
1220 Json(json!({
1221 "error": "Failed to update cost budget",
1222 "message": format!("{}", e)
1223 }))
1224 )),
1225 }
1226}
1227
1228#[delete("/platform/<platform_id>/cost_budgets/<id>")]
1230pub async fn delete_cost_budget(
1231 platform_id: i64,
1232 id: i64,
1233 db_manager: &State<Arc<DatabaseManager>>,
1234) -> Result<Json<Value>, (Status, Json<Value>)> {
1235 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1237 Ok(platform) => platform,
1238 Err(_) => {
1239 return Err((
1240 Status::NotFound,
1241 Json(json!({
1242 "error": "Platform not found",
1243 "message": format!("Platform with ID {} does not exist", platform_id)
1244 }))
1245 ));
1246 }
1247 };
1248
1249 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1251 Ok(pool) => pool,
1252 Err(_) => {
1253 return Err((
1254 Status::InternalServerError,
1255 Json(json!({
1256 "error": "Database error",
1257 "message": "Failed to connect to platform database"
1258 }))
1259 ));
1260 }
1261 };
1262
1263 match db::cost::delete_cost_budget(&pool, id).await {
1264 Ok(_) => Ok(Json(json!({ "status": "deleted" }))),
1265 Err(e) => Err((
1266 Status::InternalServerError,
1267 Json(json!({
1268 "error": "Failed to delete cost budget",
1269 "message": format!("{}", e)
1270 }))
1271 )),
1272 }
1273}
1274
1275#[get("/platform/<platform_id>/cost_projections?<page>&<per_page>")]
1279pub async fn list_cost_projections(
1280 platform_id: i64,
1281 page: Option<i64>,
1282 per_page: Option<i64>,
1283 db_manager: &State<Arc<DatabaseManager>>,
1284) -> Result<Json<Value>, (Status, Json<Value>)> {
1285 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1287 Ok(platform) => platform,
1288 Err(_) => {
1289 return Err((
1290 Status::NotFound,
1291 Json(json!({
1292 "error": "Platform not found",
1293 "message": format!("Platform with ID {} does not exist", platform_id)
1294 }))
1295 ));
1296 }
1297 };
1298
1299 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1301 Ok(pool) => pool,
1302 Err(_) => {
1303 return Err((
1304 Status::InternalServerError,
1305 Json(json!({
1306 "error": "Database error",
1307 "message": "Failed to connect to platform database"
1308 }))
1309 ));
1310 }
1311 };
1312
1313 match (page, per_page) {
1314 (Some(p), Some(pp)) => {
1315 let projections = match db::cost::list_cost_projections(&pool, p, pp).await {
1316 Ok(projections) => projections,
1317 Err(_) => {
1318 return Err((
1319 Status::InternalServerError,
1320 Json(json!({
1321 "error": "Database error",
1322 "message": "Failed to retrieve cost projections"
1323 }))
1324 ));
1325 }
1326 };
1327
1328 let response = json!({
1329 "cost_projections": projections,
1330 "pagination": {
1331 "page": p,
1332 "per_page": pp
1333 }
1334 });
1335
1336 Ok(Json(response))
1337 }
1338 _ => Err((
1339 Status::BadRequest,
1340 Json(json!({
1341 "error": "Missing pagination parameters",
1342 "message": "Please provide both 'page' and 'per_page' parameters"
1343 }))
1344 ))
1345 }
1346}
1347
1348#[get("/platform/<platform_id>/cost_projections/<id>")]
1350pub async fn get_cost_projection(
1351 platform_id: i64,
1352 id: i64,
1353 db_manager: &State<Arc<DatabaseManager>>,
1354) -> Result<Json<CostProjection>, (Status, Json<Value>)> {
1355 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1357 Ok(platform) => platform,
1358 Err(_) => {
1359 return Err((
1360 Status::NotFound,
1361 Json(json!({
1362 "error": "Platform not found",
1363 "message": format!("Platform with ID {} does not exist", platform_id)
1364 }))
1365 ));
1366 }
1367 };
1368
1369 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1371 Ok(pool) => pool,
1372 Err(_) => {
1373 return Err((
1374 Status::InternalServerError,
1375 Json(json!({
1376 "error": "Database error",
1377 "message": "Failed to connect to platform database"
1378 }))
1379 ));
1380 }
1381 };
1382
1383 match db::cost::get_cost_projection_by_id(&pool, id).await {
1384 Ok(projection) => Ok(Json(projection)),
1385 Err(_) => Err((
1386 Status::NotFound,
1387 Json(json!({
1388 "error": "Cost projection not found",
1389 "message": format!("Cost projection with ID {} could not be found", id)
1390 }))
1391 )),
1392 }
1393}
1394
1395#[post("/platform/<platform_id>/cost_projections", format = "json", data = "<request>")]
1397pub async fn create_cost_projection(
1398 platform_id: i64,
1399 request: Json<CreateCostProjectionRequest>,
1400 db_manager: &State<Arc<DatabaseManager>>,
1401) -> Result<Json<CostProjection>, (Status, Json<Value>)> {
1402 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1404 Ok(platform) => platform,
1405 Err(_) => {
1406 return Err((
1407 Status::NotFound,
1408 Json(json!({
1409 "error": "Platform not found",
1410 "message": format!("Platform with ID {} does not exist", platform_id)
1411 }))
1412 ));
1413 }
1414 };
1415
1416 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1418 Ok(pool) => pool,
1419 Err(_) => {
1420 return Err((
1421 Status::InternalServerError,
1422 Json(json!({
1423 "error": "Database error",
1424 "message": "Failed to connect to platform database"
1425 }))
1426 ));
1427 }
1428 };
1429
1430 match db::cost::create_cost_projection(
1431 &pool,
1432 request.org_id,
1433 request.app_id,
1434 &request.projection_period,
1435 request.start_date,
1436 request.end_date,
1437 request.projected_cost,
1438 &request.currency,
1439 &request.projection_model,
1440 request.confidence_level,
1441 request.metadata.as_deref(),
1442 ).await {
1443 Ok(projection) => Ok(Json(projection)),
1444 Err(e) => Err((
1445 Status::InternalServerError,
1446 Json(json!({
1447 "error": "Failed to create cost projection",
1448 "message": format!("{}", e)
1449 }))
1450 )),
1451 }
1452}
1453
1454#[delete("/platform/<platform_id>/cost_projections/<id>")]
1456pub async fn delete_cost_projection(
1457 platform_id: i64,
1458 id: i64,
1459 db_manager: &State<Arc<DatabaseManager>>,
1460) -> Result<Json<Value>, (Status, Json<Value>)> {
1461 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1463 Ok(platform) => platform,
1464 Err(_) => {
1465 return Err((
1466 Status::NotFound,
1467 Json(json!({
1468 "error": "Platform not found",
1469 "message": format!("Platform with ID {} does not exist", platform_id)
1470 }))
1471 ));
1472 }
1473 };
1474
1475 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1477 Ok(pool) => pool,
1478 Err(_) => {
1479 return Err((
1480 Status::InternalServerError,
1481 Json(json!({
1482 "error": "Database error",
1483 "message": "Failed to connect to platform database"
1484 }))
1485 ));
1486 }
1487 };
1488
1489 match db::cost::delete_cost_projection(&pool, id).await {
1490 Ok(_) => Ok(Json(json!({ "status": "deleted" }))),
1491 Err(e) => Err((
1492 Status::InternalServerError,
1493 Json(json!({
1494 "error": "Failed to delete cost projection",
1495 "message": format!("{}", e)
1496 }))
1497 )),
1498 }
1499}
1500
1501#[get("/platform/<platform_id>/resource_pricing?<page>&<per_page>&<resource_type_id>&<provider_id>&<region_id>&<pricing_model>&<tier_name>")]
1505pub async fn list_resource_pricing(
1506 platform_id: i64,
1507 page: Option<i64>,
1508 per_page: Option<i64>,
1509 resource_type_id: Option<i32>,
1510 provider_id: Option<i64>,
1511 region_id: Option<i64>,
1512 pricing_model: Option<String>,
1513 tier_name: Option<String>,
1514 db_manager: &State<Arc<DatabaseManager>>,
1515) -> Result<Json<Value>, (Status, Json<Value>)> {
1516 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1518 Ok(platform) => platform,
1519 Err(_) => {
1520 return Err((
1521 Status::NotFound,
1522 Json(json!({
1523 "error": "Platform not found",
1524 "message": format!("Platform with ID {} does not exist", platform_id)
1525 }))
1526 ));
1527 }
1528 };
1529
1530 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1532 Ok(pool) => pool,
1533 Err(_) => {
1534 return Err((
1535 Status::InternalServerError,
1536 Json(json!({
1537 "error": "Database error",
1538 "message": "Failed to connect to platform database"
1539 }))
1540 ));
1541 }
1542 };
1543
1544 match (page, per_page) {
1545 (Some(p), Some(pp)) => {
1546 let pricing = match db::cost::list_resource_pricing(
1547 &pool, p, pp, resource_type_id, provider_id, region_id, pricing_model.as_deref(), tier_name.as_deref()
1548 ).await {
1549 Ok(pricing) => pricing,
1550 Err(_) => {
1551 return Err((
1552 Status::InternalServerError,
1553 Json(json!({
1554 "error": "Database error",
1555 "message": "Failed to retrieve resource pricing"
1556 }))
1557 ));
1558 }
1559 };
1560
1561 let response = json!({
1562 "resource_pricing": pricing,
1563 "pagination": {
1564 "page": p,
1565 "per_page": pp
1566 }
1567 });
1568
1569 Ok(Json(response))
1570 }
1571 _ => Err((
1572 Status::BadRequest,
1573 Json(json!({
1574 "error": "Missing pagination parameters",
1575 "message": "Please provide both 'page' and 'per_page' parameters"
1576 }))
1577 ))
1578 }
1579}
1580
1581#[get("/platform/<platform_id>/resource_pricing/<id>")]
1583pub async fn get_resource_pricing(
1584 platform_id: i64,
1585 id: i64,
1586 db_manager: &State<Arc<DatabaseManager>>,
1587) -> Result<Json<ResourcePricing>, (Status, Json<Value>)> {
1588 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1590 Ok(platform) => platform,
1591 Err(_) => {
1592 return Err((
1593 Status::NotFound,
1594 Json(json!({
1595 "error": "Platform not found",
1596 "message": format!("Platform with ID {} does not exist", platform_id)
1597 }))
1598 ));
1599 }
1600 };
1601
1602 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1604 Ok(pool) => pool,
1605 Err(_) => {
1606 return Err((
1607 Status::InternalServerError,
1608 Json(json!({
1609 "error": "Database error",
1610 "message": "Failed to connect to platform database"
1611 }))
1612 ));
1613 }
1614 };
1615
1616 match db::cost::get_resource_pricing_by_id(&pool, id).await {
1617 Ok(pricing) => Ok(Json(pricing)),
1618 Err(_) => Err((
1619 Status::NotFound,
1620 Json(json!({
1621 "error": "Resource pricing not found",
1622 "message": format!("Resource pricing with ID {} could not be found", id)
1623 }))
1624 )),
1625 }
1626}
1627
1628#[post("/platform/<platform_id>/resource_pricing", format = "json", data = "<request>")]
1630pub async fn create_resource_pricing(
1631 platform_id: i64,
1632 request: Json<CreateResourcePricingRequest>,
1633 db_manager: &State<Arc<DatabaseManager>>,
1634) -> Result<Json<ResourcePricing>, (Status, Json<Value>)> {
1635 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1637 Ok(platform) => platform,
1638 Err(_) => {
1639 return Err((
1640 Status::NotFound,
1641 Json(json!({
1642 "error": "Platform not found",
1643 "message": format!("Platform with ID {} does not exist", platform_id)
1644 }))
1645 ));
1646 }
1647 };
1648
1649 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1651 Ok(pool) => pool,
1652 Err(_) => {
1653 return Err((
1654 Status::InternalServerError,
1655 Json(json!({
1656 "error": "Database error",
1657 "message": "Failed to connect to platform database"
1658 }))
1659 ));
1660 }
1661 };
1662
1663 match db::cost::create_resource_pricing(
1664 &pool,
1665 request.resource_type_id,
1666 request.provider_id,
1667 request.region_id,
1668 &request.tier_name,
1669 request.unit_price,
1670 &request.currency,
1671 request.effective_from,
1672 request.effective_to,
1673 &request.pricing_model,
1674 request.commitment_period.as_deref(),
1675 request.volume_discount_tiers.as_deref(),
1676 ).await {
1677 Ok(pricing) => Ok(Json(pricing)),
1678 Err(e) => Err((
1679 Status::InternalServerError,
1680 Json(json!({
1681 "error": "Failed to create resource pricing",
1682 "message": format!("{}", e)
1683 }))
1684 )),
1685 }
1686}
1687
1688#[put("/platform/<platform_id>/resource_pricing/<id>", format = "json", data = "<request>")]
1690pub async fn update_resource_pricing(
1691 platform_id: i64,
1692 id: i64,
1693 request: Json<UpdateResourcePricingRequest>,
1694 db_manager: &State<Arc<DatabaseManager>>,
1695) -> Result<Json<ResourcePricing>, (Status, Json<Value>)> {
1696 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1698 Ok(platform) => platform,
1699 Err(_) => {
1700 return Err((
1701 Status::NotFound,
1702 Json(json!({
1703 "error": "Platform not found",
1704 "message": format!("Platform with ID {} does not exist", platform_id)
1705 }))
1706 ));
1707 }
1708 };
1709
1710 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1712 Ok(pool) => pool,
1713 Err(_) => {
1714 return Err((
1715 Status::InternalServerError,
1716 Json(json!({
1717 "error": "Database error",
1718 "message": "Failed to connect to platform database"
1719 }))
1720 ));
1721 }
1722 };
1723
1724 match db::cost::update_resource_pricing(
1725 &pool,
1726 id,
1727 request.unit_price,
1728 request.effective_to,
1729 request.volume_discount_tiers.as_deref(),
1730 ).await {
1731 Ok(pricing) => Ok(Json(pricing)),
1732 Err(e) => Err((
1733 Status::InternalServerError,
1734 Json(json!({
1735 "error": "Failed to update resource pricing",
1736 "message": format!("{}", e)
1737 }))
1738 )),
1739 }
1740}
1741
1742#[delete("/platform/<platform_id>/resource_pricing/<id>")]
1744pub async fn delete_resource_pricing(
1745 platform_id: i64,
1746 id: i64,
1747 db_manager: &State<Arc<DatabaseManager>>,
1748) -> Result<Json<Value>, (Status, Json<Value>)> {
1749 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1751 Ok(platform) => platform,
1752 Err(_) => {
1753 return Err((
1754 Status::NotFound,
1755 Json(json!({
1756 "error": "Platform not found",
1757 "message": format!("Platform with ID {} does not exist", platform_id)
1758 }))
1759 ));
1760 }
1761 };
1762
1763 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1765 Ok(pool) => pool,
1766 Err(_) => {
1767 return Err((
1768 Status::InternalServerError,
1769 Json(json!({
1770 "error": "Database error",
1771 "message": "Failed to connect to platform database"
1772 }))
1773 ));
1774 }
1775 };
1776
1777 match db::cost::delete_resource_pricing(&pool, id).await {
1778 Ok(_) => Ok(Json(json!({ "status": "deleted" }))),
1779 Err(e) => Err((
1780 Status::InternalServerError,
1781 Json(json!({
1782 "error": "Failed to delete resource pricing",
1783 "message": format!("{}", e)
1784 }))
1785 )),
1786 }
1787}
1788
1789#[get("/platform/<platform_id>/cost_allocation_tags/<resource_id>/<resource_type>")]
1793pub async fn get_cost_allocation_tags(
1794 platform_id: i64,
1795 resource_id: i64,
1796 resource_type: String,
1797 db_manager: &State<Arc<DatabaseManager>>,
1798) -> Result<Json<Vec<CostAllocationTag>>, (Status, Json<Value>)> {
1799 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1801 Ok(platform) => platform,
1802 Err(_) => {
1803 return Err((
1804 Status::NotFound,
1805 Json(json!({
1806 "error": "Platform not found",
1807 "message": format!("Platform with ID {} does not exist", platform_id)
1808 }))
1809 ));
1810 }
1811 };
1812
1813 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1815 Ok(pool) => pool,
1816 Err(_) => {
1817 return Err((
1818 Status::InternalServerError,
1819 Json(json!({
1820 "error": "Database error",
1821 "message": "Failed to connect to platform database"
1822 }))
1823 ));
1824 }
1825 };
1826
1827 match db::cost::get_cost_allocation_tags(&pool, resource_id, &resource_type).await {
1828 Ok(tags) => Ok(Json(tags)),
1829 Err(e) => Err((
1830 Status::InternalServerError,
1831 Json(json!({
1832 "error": "Failed to retrieve cost allocation tags",
1833 "message": format!("{}", e)
1834 }))
1835 )),
1836 }
1837}
1838
1839#[post("/platform/<platform_id>/cost_allocation_tags", format = "json", data = "<request>")]
1841pub async fn create_cost_allocation_tag(
1842 platform_id: i64,
1843 request: Json<CreateCostAllocationTagRequest>,
1844 db_manager: &State<Arc<DatabaseManager>>,
1845) -> Result<Json<CostAllocationTag>, (Status, Json<Value>)> {
1846 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1848 Ok(platform) => platform,
1849 Err(_) => {
1850 return Err((
1851 Status::NotFound,
1852 Json(json!({
1853 "error": "Platform not found",
1854 "message": format!("Platform with ID {} does not exist", platform_id)
1855 }))
1856 ));
1857 }
1858 };
1859
1860 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1862 Ok(pool) => pool,
1863 Err(_) => {
1864 return Err((
1865 Status::InternalServerError,
1866 Json(json!({
1867 "error": "Database error",
1868 "message": "Failed to connect to platform database"
1869 }))
1870 ));
1871 }
1872 };
1873
1874 match db::cost::create_cost_allocation_tag(
1875 &pool,
1876 &request.tag_key,
1877 &request.tag_value,
1878 request.resource_id,
1879 &request.resource_type,
1880 ).await {
1881 Ok(tag) => Ok(Json(tag)),
1882 Err(e) => Err((
1883 Status::InternalServerError,
1884 Json(json!({
1885 "error": "Failed to create cost allocation tag",
1886 "message": format!("{}", e)
1887 }))
1888 )),
1889 }
1890}
1891
1892#[delete("/platform/<platform_id>/cost_allocation_tags/<id>")]
1894pub async fn delete_cost_allocation_tag(
1895 platform_id: i64,
1896 id: i64,
1897 db_manager: &State<Arc<DatabaseManager>>,
1898) -> Result<Json<Value>, (Status, Json<Value>)> {
1899 let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1901 Ok(platform) => platform,
1902 Err(_) => {
1903 return Err((
1904 Status::NotFound,
1905 Json(json!({
1906 "error": "Platform not found",
1907 "message": format!("Platform with ID {} does not exist", platform_id)
1908 }))
1909 ));
1910 }
1911 };
1912
1913 let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1915 Ok(pool) => pool,
1916 Err(_) => {
1917 return Err((
1918 Status::InternalServerError,
1919 Json(json!({
1920 "error": "Database error",
1921 "message": "Failed to connect to platform database"
1922 }))
1923 ));
1924 }
1925 };
1926
1927 match db::cost::delete_cost_allocation_tag(&pool, id).await {
1928 Ok(_) => Ok(Json(json!({ "status": "deleted" }))),
1929 Err(e) => Err((
1930 Status::InternalServerError,
1931 Json(json!({
1932 "error": "Failed to delete cost allocation tag",
1933 "message": format!("{}", e)
1934 }))
1935 )),
1936 }
1937}