tmf656_slice/
db.rs

1//! Database operations for TMF656 Slice Management
2
3use 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
9// Helper to convert sqlx::Error to TmfError
10fn map_sqlx_error(err: sqlx::Error) -> TmfError {
11    TmfError::Database(err.to_string())
12}
13
14/// Parse slice state from database string
15fn 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
25/// Convert slice state to database string
26fn 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
35/// Parse slice type from database string
36fn 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
46/// Convert slice type to database string
47fn 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
58/// Get all network slices
59pub 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,    // Load separately if needed
85            network_functions: None, // Load separately if needed
86            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
94/// Get network slice by ID
95pub 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,    // Load separately if needed
121        network_functions: None, // Load separately if needed
122        activation_date: row.get::<Option<DateTime<Utc>>, _>("activation_date"),
123        termination_date: row.get::<Option<DateTime<Utc>>, _>("termination_date"),
124    })
125}
126
127/// Create a new network slice
128pub 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    // Fetch the created network slice
153    get_network_slice_by_id(pool, id).await
154}
155
156/// Update a network slice
157pub 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    // Fetch the updated network slice
181    get_network_slice_by_id(pool, id).await
182}
183
184/// Delete a network slice
185pub 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}