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

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