1use crate::models::{CreateNetworkSliceRequest, NetworkSlice, SliceState, SliceType};
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_slice_state(s: &str) -> SliceState {
16 match s.to_uppercase().as_str() {
17 "PLANNED" => SliceState::Planned,
18 "ACTIVE" => SliceState::Active,
19 "INACTIVE" => SliceState::Inactive,
20 "TERMINATED" => SliceState::Terminated,
21 _ => SliceState::Planned,
22 }
23}
24
25fn slice_state_to_string(state: &SliceState) -> String {
27 match state {
28 SliceState::Planned => "PLANNED".to_string(),
29 SliceState::Active => "ACTIVE".to_string(),
30 SliceState::Inactive => "INACTIVE".to_string(),
31 SliceState::Terminated => "TERMINATED".to_string(),
32 }
33}
34
35fn parse_slice_type(s: &str) -> SliceType {
37 match s.to_uppercase().as_str() {
38 "ENHANCED_MOBILE_BROADBAND" => SliceType::EnhancedMobileBroadband,
39 "ULTRA_RELIABLE_LOW_LATENCY" => SliceType::UltraReliableLowLatency,
40 "MASSIVE_MACHINE_TYPE_COMMUNICATIONS" => SliceType::MassiveMachineTypeCommunications,
41 "CUSTOM" => SliceType::Custom,
42 _ => SliceType::Custom,
43 }
44}
45
46fn slice_type_to_string(slice_type: &SliceType) -> String {
48 match slice_type {
49 SliceType::EnhancedMobileBroadband => "ENHANCED_MOBILE_BROADBAND".to_string(),
50 SliceType::UltraReliableLowLatency => "ULTRA_RELIABLE_LOW_LATENCY".to_string(),
51 SliceType::MassiveMachineTypeCommunications => {
52 "MASSIVE_MACHINE_TYPE_COMMUNICATIONS".to_string()
53 }
54 SliceType::Custom => "CUSTOM".to_string(),
55 }
56}
57
58pub async fn get_network_slices(pool: &Pool<Postgres>) -> TmfResult<Vec<NetworkSlice>> {
60 let rows = sqlx::query(
61 "SELECT id, name, description, version, state, slice_type,
62 activation_date, termination_date, href, last_update
63 FROM network_slices ORDER BY activation_date DESC",
64 )
65 .fetch_all(pool)
66 .await
67 .map_err(map_sqlx_error)?;
68
69 let mut slices = Vec::new();
70 for row in rows {
71 slices.push(NetworkSlice {
72 base: tmf_apis_core::BaseEntity {
73 id: row.get::<Uuid, _>("id"),
74 href: row.get::<Option<String>, _>("href"),
75 name: row.get::<String, _>("name"),
76 description: row.get::<Option<String>, _>("description"),
77 version: row.get::<Option<String>, _>("version"),
78 lifecycle_status: tmf_apis_core::LifecycleStatus::Active,
79 last_update: row.get::<Option<DateTime<Utc>>, _>("last_update"),
80 valid_for: None,
81 },
82 state: parse_slice_state(&row.get::<String, _>("state")),
83 slice_type: parse_slice_type(&row.get::<String, _>("slice_type")),
84 sla_parameters: None, network_functions: None, activation_date: row.get::<Option<DateTime<Utc>>, _>("activation_date"),
87 termination_date: row.get::<Option<DateTime<Utc>>, _>("termination_date"),
88 });
89 }
90
91 Ok(slices)
92}
93
94pub async fn get_network_slice_by_id(pool: &Pool<Postgres>, id: Uuid) -> TmfResult<NetworkSlice> {
96 let row = sqlx::query(
97 "SELECT id, name, description, version, state, slice_type,
98 activation_date, termination_date, href, last_update
99 FROM network_slices WHERE id = $1",
100 )
101 .bind(id)
102 .fetch_optional(pool)
103 .await
104 .map_err(map_sqlx_error)?
105 .ok_or_else(|| TmfError::NotFound(format!("Network slice with id {} not found", id)))?;
106
107 Ok(NetworkSlice {
108 base: tmf_apis_core::BaseEntity {
109 id: row.get::<Uuid, _>("id"),
110 href: row.get::<Option<String>, _>("href"),
111 name: row.get::<String, _>("name"),
112 description: row.get::<Option<String>, _>("description"),
113 version: row.get::<Option<String>, _>("version"),
114 lifecycle_status: tmf_apis_core::LifecycleStatus::Active,
115 last_update: row.get::<Option<DateTime<Utc>>, _>("last_update"),
116 valid_for: None,
117 },
118 state: parse_slice_state(&row.get::<String, _>("state")),
119 slice_type: parse_slice_type(&row.get::<String, _>("slice_type")),
120 sla_parameters: None, network_functions: None, activation_date: row.get::<Option<DateTime<Utc>>, _>("activation_date"),
123 termination_date: row.get::<Option<DateTime<Utc>>, _>("termination_date"),
124 })
125}
126
127pub async fn create_network_slice(
129 pool: &Pool<Postgres>,
130 request: CreateNetworkSliceRequest,
131) -> TmfResult<NetworkSlice> {
132 let id = Uuid::new_v4();
133 let href = Some(format!("/tmf-api/sliceManagement/v4/networkSlice/{}", id));
134
135 sqlx::query(
136 "INSERT INTO network_slices (id, name, description, version, state, slice_type,
137 activation_date, href)
138 VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
139 )
140 .bind(id)
141 .bind(&request.name)
142 .bind(&request.description)
143 .bind(&request.version)
144 .bind(slice_state_to_string(&SliceState::Planned))
145 .bind(slice_type_to_string(&request.slice_type))
146 .bind(request.activation_date)
147 .bind(&href)
148 .execute(pool)
149 .await
150 .map_err(map_sqlx_error)?;
151
152 get_network_slice_by_id(pool, id).await
154}
155
156pub async fn update_network_slice(
158 pool: &Pool<Postgres>,
159 id: Uuid,
160 state: Option<SliceState>,
161 activation_date: Option<DateTime<Utc>>,
162 termination_date: Option<DateTime<Utc>>,
163) -> TmfResult<NetworkSlice> {
164 sqlx::query(
165 "UPDATE network_slices SET
166 state = COALESCE($1, state),
167 activation_date = COALESCE($2, activation_date),
168 termination_date = COALESCE($3, termination_date),
169 last_update = CURRENT_TIMESTAMP
170 WHERE id = $4",
171 )
172 .bind(state.as_ref().map(slice_state_to_string))
173 .bind(activation_date)
174 .bind(termination_date)
175 .bind(id)
176 .execute(pool)
177 .await
178 .map_err(map_sqlx_error)?;
179
180 get_network_slice_by_id(pool, id).await
182}
183
184pub async fn delete_network_slice(pool: &Pool<Postgres>, id: Uuid) -> TmfResult<()> {
186 let result = sqlx::query("DELETE FROM network_slices WHERE id = $1")
187 .bind(id)
188 .execute(pool)
189 .await
190 .map_err(map_sqlx_error)?;
191
192 if result.rows_affected() == 0 {
193 return Err(TmfError::NotFound(format!(
194 "Network slice with id {} not found",
195 id
196 )));
197 }
198
199 Ok(())
200}