omni_orchestrator/schemas/v1/db/queries/
region.rs

1use crate::models::region::Region;
2use crate::models::provider::ProviderRegion;
3use anyhow::Context;
4use sqlx::{MySql, Pool};
5
6/// Retrieves a paginated list of deployment regions.
7///
8/// This function fetches regions from the database with optional pagination support.
9/// Results are ordered by creation time with the most recently created regions first.
10///
11/// # Arguments
12///
13/// * `pool` - Database connection pool for executing the query
14/// * `limit` - Optional maximum number of regions to return (defaults to 100 if not specified)
15/// * `offset` - Optional number of regions to skip (for pagination)
16///
17/// # Returns
18///
19/// * `Ok(Vec<Region>)` - Successfully retrieved list of regions
20/// * `Err(anyhow::Error)` - Failed to fetch regions
21///
22/// # Dynamic Query Building
23///
24/// This function uses SQLx's QueryBuilder to dynamically construct a SQL query
25/// based on whether pagination parameters are provided. This approach is more
26/// efficient than building strings manually and protects against SQL injection.
27///
28/// # Pagination
29///
30/// When both `limit` and `offset` are provided, standard SQL pagination is applied.
31/// If only `limit` is provided, just the first N records are returned.
32/// If neither is provided, all regions are returned (with a safety limit of 100).
33pub async fn list_regions(
34    pool: &Pool<MySql>,
35    limit: Option<i64>,
36    offset: Option<i64>,
37) -> anyhow::Result<Vec<Region>> {
38    let regions = sqlx::query_as::<_, Region>(
39        "SELECT * FROM regions ORDER BY created_at DESC LIMIT ? OFFSET ?",
40    )
41    .bind(limit.unwrap_or(100))
42    .bind(offset.unwrap_or(0))
43    .fetch_all(pool)
44    .await
45    .context("Failed to fetch regions")?;
46
47    Ok(regions)
48}
49
50pub async fn list_provider_regions(
51    pool: &Pool<MySql>,
52) -> anyhow::Result<Vec<ProviderRegion>> {
53    let regions = sqlx::query_as::<_, ProviderRegion>(
54        "SELECT regions.*, providers.name AS provider_name, providers_regions.status AS binding_status 
55         FROM regions 
56         JOIN providers ON regions.provider = providers.id 
57         JOIN providers_regions ON regions.id = providers_regions.region_id AND providers.id = providers_regions.provider_id",
58    )
59    .fetch_all(pool)
60    .await
61    .context("Failed to fetch provider regions")?;
62
63    Ok(regions)
64}
65
66/// Retrieves a specific region by its unique identifier.
67///
68/// This function fetches detailed information about a single region record.
69/// It's typically used when specific region details are needed, such as
70/// for displaying region information or resource allocation.
71///
72/// # Arguments
73///
74/// * `pool` - Database connection pool for executing the query
75/// * `id` - Unique identifier of the region to retrieve
76///
77/// # Returns
78///
79/// * `Ok(Region)` - Successfully retrieved region information
80/// * `Err(anyhow::Error)` - Failed to fetch region (including if not found)
81///
82/// # Error Handling
83///
84/// Returns an error if no region with the given ID exists or if a database
85/// error occurs during the query execution.
86pub async fn get_region_by_id(pool: &Pool<MySql>, id: i64) -> anyhow::Result<Region> {
87    let region = sqlx::query_as::<_, Region>("SELECT * FROM regions WHERE id = ?")
88        .bind(id)
89        .fetch_one(pool)
90        .await
91        .context("Failed to fetch region")?;
92
93    Ok(region)
94}
95
96/// Creates a new deployment region in the system.
97///
98/// This function registers a new region for application deployments.
99/// Regions typically represent geographical deployment locations or
100/// distinct cloud provider environments.
101///
102/// # Arguments
103///
104/// * `pool` - Database connection pool for executing the query
105/// * `name` - Name of the region (typically a geographical identifier like "us-east", "eu-west")
106/// * `provider` - Cloud or infrastructure provider (e.g., "aws", "gcp", "azure", "on-prem")
107/// * `status` - Initial status of the region (e.g., "provisioning", "active", "maintenance")
108///
109/// # Returns
110///
111/// * `Ok(Region)` - Successfully created region record
112/// * `Err(anyhow::Error)` - Failed to create region record
113///
114/// # Transaction Handling
115///
116/// This function uses a database transaction to ensure atomicity of the operation.
117/// If any part of the operation fails, the entire operation is rolled back.
118///
119/// # Common Region States
120///
121/// Typical region status values include:
122/// - "provisioning" - Region is being set up and not yet ready for deployments
123/// - "active" - Region is fully operational and can accept deployments
124/// - "maintenance" - Region is temporarily unavailable for new deployments
125/// - "deprecated" - Region is being phased out, no new deployments accepted
126/// - "unavailable" - Region is not currently operational
127pub async fn create_region(
128    pool: &Pool<MySql>,
129    name: &str,
130    provider: &str,
131    status: &str,
132) -> anyhow::Result<Region> {
133    let mut tx = pool.begin().await?;
134
135    let region = sqlx::query_as::<_, Region>(
136        "INSERT INTO regions (name, provider, status) VALUES (?, ?, ?)",
137    )
138    .bind(name)
139    .bind(provider)
140    .bind(status)
141    .fetch_one(&mut *tx)
142    .await
143    .context("Failed to create region")?;
144
145    tx.commit().await?;
146    Ok(region)
147}
148
149/// Updates the status of an existing deployment region.
150///
151/// This function changes the operational status of a region, which affects
152/// whether new deployments can be directed to it. Status changes are critical
153/// operations that can affect application availability and deployment strategies.
154///
155/// # Arguments
156///
157/// * `pool` - Database connection pool for executing the query
158/// * `id` - Unique identifier of the region to update
159/// * `status` - New status for the region (e.g., "active", "maintenance", "unavailable")
160///
161/// # Returns
162///
163/// * `Ok(Region)` - Successfully updated region record
164/// * `Err(anyhow::Error)` - Failed to update region
165///
166/// # Transaction Handling
167///
168/// This function uses a database transaction to ensure atomicity of the operation.
169/// If any part of the operation fails, the entire operation is rolled back.
170///
171/// # Operational Impact
172///
173/// Changing a region's status may have significant operational impacts:
174/// - Setting to "maintenance" or "unavailable" prevents new deployments
175/// - Status changes should be coordinated with deployment schedules
176/// - Monitoring systems may need to be updated based on region status
177/// - Load balancers may need reconfiguration after status changes
178pub async fn update_region_status(
179    pool: &Pool<MySql>,
180    id: i64,
181    status: &str,
182) -> anyhow::Result<Region> {
183    let mut tx = pool.begin().await?;
184
185    let region = sqlx::query_as::<_, Region>("UPDATE regions SET status = ? WHERE id = ?")
186        .bind(status)
187        .bind(id)
188        .fetch_one(&mut *tx)
189        .await
190        .context("Failed to update region status")?;
191
192    tx.commit().await?;
193    Ok(region)
194}
195
196/// Deletes a deployment region from the system.
197///
198/// This function permanently removes a region record from the database.
199/// It should be used with extreme caution, as it may affect deployed applications
200/// and infrastructure allocation.
201///
202/// # Arguments
203///
204/// * `pool` - Database connection pool for executing the query
205/// * `id` - Unique identifier of the region to delete
206///
207/// # Returns
208///
209/// * `Ok(())` - Successfully deleted the region
210/// * `Err(anyhow::Error)` - Failed to delete the region
211///
212/// # Warning
213///
214/// This operation is irreversible and potentially dangerous. Instead of deleting
215/// regions, consider changing their status to "deprecated" or "unavailable" first,
216/// and ensure no active deployments exist in the region before deletion.
217///
218/// # Cascading Effects
219///
220/// Depending on the database schema and application logic:
221/// - Deployed applications in this region may lose their region reference
222/// - Foreign key constraints may prevent deletion if the region is in use
223/// - Monitoring, billing, and operational systems may be affected
224///
225/// # Transaction Handling
226///
227/// This function uses a database transaction to ensure atomicity of the operation.
228/// If any part of the operation fails, the entire operation is rolled back.
229pub async fn delete_region(pool: &Pool<MySql>, id: i64) -> anyhow::Result<()> {
230    let mut tx = pool.begin().await?;
231
232    sqlx::query("DELETE FROM regions WHERE id = ?")
233        .bind(id)
234        .execute(&mut *tx)
235        .await
236        .context("Failed to delete region")?;
237
238    tx.commit().await?;
239    Ok(())
240}
241
242/// Retrieves all active deployment regions.
243///
244/// This function fetches all regions with a status of "active", indicating
245/// they are available for new deployments. It's typically used for deployment
246/// target selection and region availability displays.
247///
248/// # Arguments
249///
250/// * `pool` - Database connection pool for executing the query
251///
252/// # Returns
253///
254/// * `Ok(Vec<Region>)` - Successfully retrieved list of active regions
255/// * `Err(anyhow::Error)` - Failed to fetch active regions
256///
257/// # Use Cases
258///
259/// Common use cases include:
260/// - Populating region selection dropdowns in deployment interfaces
261/// - Determining valid deployment targets for automated processes
262/// - Calculating resource availability across active regions
263/// - Health status dashboards showing operational deployment locations
264///
265/// # Query Details
266///
267/// Results are filtered by status="active" and ordered by creation time,
268/// with the most recently created regions appearing first in the list.
269pub async fn get_active_regions(pool: &Pool<MySql>) -> anyhow::Result<Vec<Region>> {
270    let regions = sqlx::query_as::<_, Region>(
271        "SELECT * FROM regions WHERE status = 'active' ORDER BY created_at DESC",
272    )
273    .fetch_all(pool)
274    .await
275    .context("Failed to fetch active regions")?;
276
277    Ok(regions)
278}