1use crate::models::{CreateUsageRequest, Usage, UsageState};
4use chrono::{DateTime, Utc};
5use sqlx::{Pool, Postgres, Row};
6use tmf_apis_core::{TmfError, TmfResult};
7use uuid::Uuid;
8
9fn map_sqlx_error(err: sqlx::Error) -> TmfError {
11 TmfError::Database(err.to_string())
12}
13
14fn parse_usage_state(s: &str) -> UsageState {
16 match s.to_uppercase().as_str() {
17 "CAPTURED" => UsageState::Captured,
18 "RATED" => UsageState::Rated,
19 "BILLED" => UsageState::Billed,
20 "REJECTED" => UsageState::Rejected,
21 _ => UsageState::Captured,
22 }
23}
24
25fn usage_state_to_string(state: &UsageState) -> String {
27 match state {
28 UsageState::Captured => "CAPTURED".to_string(),
29 UsageState::Rated => "RATED".to_string(),
30 UsageState::Billed => "BILLED".to_string(),
31 UsageState::Rejected => "REJECTED".to_string(),
32 }
33}
34
35pub async fn get_usages(pool: &Pool<Postgres>) -> TmfResult<Vec<Usage>> {
37 let rows = sqlx::query(
38 "SELECT id, name, description, version, state, usage_type, usage_date, start_date, end_date,
39 amount, unit, href, last_update
40 FROM usages ORDER BY usage_date DESC",
41 )
42 .fetch_all(pool)
43 .await
44 .map_err(map_sqlx_error)?;
45
46 let mut usages = Vec::new();
47 for row in rows {
48 usages.push(Usage {
49 base: tmf_apis_core::BaseEntity {
50 id: row.get::<Uuid, _>("id"),
51 href: row.get::<Option<String>, _>("href"),
52 name: row.get::<String, _>("name"),
53 description: row.get::<Option<String>, _>("description"),
54 version: row.get::<Option<String>, _>("version"),
55 lifecycle_status: tmf_apis_core::LifecycleStatus::Active,
56 last_update: row.get::<Option<DateTime<Utc>>, _>("last_update"),
57 valid_for: None,
58 },
59 state: parse_usage_state(&row.get::<String, _>("state")),
60 usage_type: row.get::<Option<String>, _>("usage_type"),
61 usage_date: row.get::<Option<DateTime<Utc>>, _>("usage_date"),
62 start_date: row.get::<Option<DateTime<Utc>>, _>("start_date"),
63 end_date: row.get::<Option<DateTime<Utc>>, _>("end_date"),
64 amount: row.get::<Option<f64>, _>("amount"),
65 unit: row.get::<Option<String>, _>("unit"),
66 product_offering: None, related_party: None, rating: None, });
70 }
71
72 Ok(usages)
73}
74
75pub async fn get_usage_by_id(pool: &Pool<Postgres>, id: Uuid) -> TmfResult<Usage> {
77 let row = sqlx::query(
78 "SELECT id, name, description, version, state, usage_type, usage_date, start_date, end_date,
79 amount, unit, href, last_update
80 FROM usages WHERE id = $1",
81 )
82 .bind(id)
83 .fetch_optional(pool)
84 .await
85 .map_err(map_sqlx_error)?
86 .ok_or_else(|| TmfError::NotFound(format!("Usage with id {} not found", id)))?;
87
88 Ok(Usage {
89 base: tmf_apis_core::BaseEntity {
90 id: row.get::<Uuid, _>("id"),
91 href: row.get::<Option<String>, _>("href"),
92 name: row.get::<String, _>("name"),
93 description: row.get::<Option<String>, _>("description"),
94 version: row.get::<Option<String>, _>("version"),
95 lifecycle_status: tmf_apis_core::LifecycleStatus::Active,
96 last_update: row.get::<Option<DateTime<Utc>>, _>("last_update"),
97 valid_for: None,
98 },
99 state: parse_usage_state(&row.get::<String, _>("state")),
100 usage_type: row.get::<Option<String>, _>("usage_type"),
101 usage_date: row.get::<Option<DateTime<Utc>>, _>("usage_date"),
102 start_date: row.get::<Option<DateTime<Utc>>, _>("start_date"),
103 end_date: row.get::<Option<DateTime<Utc>>, _>("end_date"),
104 amount: row.get::<Option<f64>, _>("amount"),
105 unit: row.get::<Option<String>, _>("unit"),
106 product_offering: None,
107 related_party: None,
108 rating: None,
109 })
110}
111
112pub async fn create_usage(pool: &Pool<Postgres>, request: CreateUsageRequest) -> TmfResult<Usage> {
114 let id = Uuid::new_v4();
115 let state = usage_state_to_string(&UsageState::Captured);
116
117 sqlx::query(
118 "INSERT INTO usages (id, name, description, version, state, usage_type, usage_date,
119 start_date, end_date, amount, unit, product_offering_id, rating_id)
120 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)",
121 )
122 .bind(id)
123 .bind(&request.name)
124 .bind(&request.description)
125 .bind(&request.version)
126 .bind(&state)
127 .bind(&request.usage_type)
128 .bind(request.usage_date)
129 .bind(request.start_date)
130 .bind(request.end_date)
131 .bind(request.amount)
132 .bind(&request.unit)
133 .bind(request.product_offering_id)
134 .bind(request.rating_id)
135 .execute(pool)
136 .await
137 .map_err(map_sqlx_error)?;
138
139 if let Some(parties) = request.related_party {
141 for party in parties {
142 let party_id = Uuid::new_v4();
143 sqlx::query(
144 "INSERT INTO usage_related_parties (id, usage_id, name, role)
145 VALUES ($1, $2, $3, $4)",
146 )
147 .bind(party_id)
148 .bind(id)
149 .bind(&party.name)
150 .bind(&party.role)
151 .execute(pool)
152 .await
153 .map_err(map_sqlx_error)?;
154 }
155 }
156
157 get_usage_by_id(pool, id).await
159}