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

1use crate::models::org::Org;
2use anyhow::Context;
3use sqlx::{MySql, Pool};
4
5/// Retrieves all organizations in the system, ordered by creation time.
6///
7/// This function fetches all organization records from the database, with
8/// the most recently created organizations first. It provides a complete view
9/// of all organizations in the system.
10///
11/// # Arguments
12///
13/// * `pool` - Database connection pool for executing the query
14///
15/// # Returns
16///
17/// * `Ok(Vec<Org>)` - Successfully retrieved list of organizations
18/// * `Err(anyhow::Error)` - Failed to fetch organizations
19///
20/// # Use Cases
21///
22/// Common use cases include:
23/// - Administrative dashboards showing all organizations
24/// - System-wide reports and analytics
25/// - Multi-tenant application management
26pub async fn list_orgs(pool: &Pool<MySql>) -> anyhow::Result<Vec<Org>> {
27    let orgs = sqlx::query_as::<_, Org>("SELECT * FROM orgs ORDER BY created_at DESC")
28        .fetch_all(pool)
29        .await
30        .context("Failed to fetch organizations")?;
31
32    Ok(orgs)
33}
34
35/// Retrieves a specific organization by its unique identifier.
36///
37/// This function fetches detailed information about a single organization record.
38/// It's typically used when specific organization details are needed, such as
39/// for displaying organization profiles or handling organization-specific operations.
40///
41/// # Arguments
42///
43/// * `pool` - Database connection pool for executing the query
44/// * `id` - Unique identifier of the organization to retrieve
45///
46/// # Returns
47///
48/// * `Ok(Org)` - Successfully retrieved organization information
49/// * `Err(anyhow::Error)` - Failed to fetch organization (including if not found)
50///
51/// # Error Handling
52///
53/// Returns an error if no organization with the given ID exists or if a database
54/// error occurs during the query execution.
55pub async fn get_org_by_id(pool: &Pool<MySql>, id: i64) -> anyhow::Result<Org> {
56    let org = sqlx::query_as::<_, Org>("SELECT * FROM orgs WHERE id = ?")
57        .bind(id)
58        .fetch_one(pool)
59        .await
60        .context("Failed to fetch organization")?;
61
62    Ok(org)
63}
64
65/// Creates a new organization in the system.
66///
67/// This function inserts a new organization record with the provided name.
68/// It uses a transaction to ensure data consistency during the creation process.
69///
70/// # Arguments
71///
72/// * `pool` - Database connection pool for executing the query
73/// * `name` - Name of the new organization
74///
75/// # Returns
76///
77/// * `Ok(Org)` - Successfully created organization record
78/// * `Err(anyhow::Error)` - Failed to create organization record
79///
80/// # Transaction Handling
81///
82/// This function uses a database transaction to ensure atomicity of the operation.
83/// If any part of the operation fails, the entire operation is rolled back.
84///
85/// # Note
86///
87/// The created organization will have system-generated values for:
88/// - `id` - Auto-incremented primary key
89/// - `created_at` - Timestamp of creation
90/// - `updated_at` - Initially same as creation timestamp
91pub async fn create_org(pool: &Pool<MySql>, name: &str) -> anyhow::Result<Org> {
92    let mut tx = pool.begin().await?;
93
94    let org = sqlx::query_as::<_, Org>("INSERT INTO orgs (name) VALUES (?)")
95        .bind(name)
96        .fetch_one(&mut *tx)
97        .await
98        .context("Failed to create organization")?;
99
100    tx.commit().await?;
101    Ok(org)
102}
103
104/// Updates an existing organization's information.
105///
106/// This function modifies an organization record with the provided name.
107/// It also updates the `updated_at` timestamp to reflect the modification time.
108///
109/// # Arguments
110///
111/// * `pool` - Database connection pool for executing the query
112/// * `id` - Unique identifier of the organization to update
113/// * `name` - New name for the organization
114///
115/// # Returns
116///
117/// * `Ok(Org)` - Successfully updated organization record
118/// * `Err(anyhow::Error)` - Failed to update organization
119///
120/// # Transaction Handling
121///
122/// This function uses a database transaction to ensure atomicity of the operation.
123/// If any part of the operation fails, the entire operation is rolled back.
124///
125/// # Error Handling
126///
127/// Returns an error if no organization with the given ID exists or if a database
128/// error occurs during the update operation.
129pub async fn update_org(pool: &Pool<MySql>, id: i64, name: &str) -> anyhow::Result<Org> {
130    let mut tx = pool.begin().await?;
131
132    let org = sqlx::query_as::<_, Org>(
133        "UPDATE orgs SET name = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
134    )
135    .bind(name)
136    .bind(id)
137    .fetch_one(&mut *tx)
138    .await
139    .context("Failed to update organization")?;
140
141    tx.commit().await?;
142    Ok(org)
143}
144
145/// Deletes an organization from the system.
146///
147/// This function permanently removes an organization record from the database.
148/// It should be used with caution, as it typically has significant implications
149/// for associated data and user access.
150///
151/// # Arguments
152///
153/// * `pool` - Database connection pool for executing the query
154/// * `id` - Unique identifier of the organization to delete
155///
156/// # Returns
157///
158/// * `Ok(())` - Successfully deleted the organization
159/// * `Err(anyhow::Error)` - Failed to delete the organization
160///
161/// # Warning
162///
163/// This operation is irreversible. Consider the implications before deleting
164/// organizations, especially those with active users or resources.
165///
166/// # Important
167///
168/// This function only deletes the organization record itself. It does not cascade
169/// delete related records such as organization members, applications, or other
170/// resources associated with the organization. Consider implementing cascading
171/// delete logic or foreign key constraints if needed.
172///
173/// # Transaction Handling
174///
175/// This function uses a database transaction to ensure atomicity of the operation.
176/// If any part of the operation fails, the entire operation is rolled back.
177pub async fn delete_org(pool: &Pool<MySql>, id: i64) -> anyhow::Result<()> {
178    let mut tx = pool.begin().await?;
179
180    sqlx::query("DELETE FROM orgs WHERE id = ?")
181        .bind(id)
182        .execute(&mut *tx)
183        .await
184        .context("Failed to delete organization")?;
185
186    tx.commit().await?;
187    Ok(())
188}
189
190/// Adds a user as a member of an organization with a specific role.
191///
192/// This function creates a relationship between a user and an organization,
193/// assigning the user a role within that organization. This role typically
194/// determines the user's permissions and access level within the organization.
195///
196/// # Arguments
197///
198/// * `pool` - Database connection pool for executing the query
199/// * `org_id` - Unique identifier of the organization
200/// * `user_id` - Unique identifier of the user to add
201/// * `role` - Role to assign to the user within the organization (e.g., "admin", "member")
202///
203/// # Returns
204///
205/// * `Ok(())` - Successfully added the user to the organization
206/// * `Err(anyhow::Error)` - Failed to add the user to the organization
207///
208/// # Uniqueness
209///
210/// This function assumes that the combination of `org_id` and `user_id`
211/// must be unique in the orgmember table. If a user is already a member
212/// of the organization, the operation will fail with a unique constraint violation.
213///
214/// # Transaction Handling
215///
216/// This function uses a database transaction to ensure atomicity of the operation.
217/// If any part of the operation fails, the entire operation is rolled back.
218pub async fn add_org_member(
219    pool: &Pool<MySql>,
220    org_id: i64,
221    user_id: i64,
222    role: &str,
223) -> anyhow::Result<()> {
224    let mut tx = pool.begin().await?;
225
226    sqlx::query("INSERT INTO orgmember (org_id, user_id, role) VALUES (?, ?, ?)")
227        .bind(org_id)
228        .bind(user_id)
229        .bind(role)
230        .execute(&mut *tx)
231        .await
232        .context("Failed to add organization member")?;
233
234    tx.commit().await?;
235    Ok(())
236}
237
238/// Removes a user from an organization.
239///
240/// This function deletes the relationship between a user and an organization,
241/// effectively revoking the user's membership and access to the organization's resources.
242///
243/// # Arguments
244///
245/// * `pool` - Database connection pool for executing the query
246/// * `org_id` - Unique identifier of the organization
247/// * `user_id` - Unique identifier of the user to remove
248///
249/// # Returns
250///
251/// * `Ok(())` - Successfully removed the user from the organization
252/// * `Err(anyhow::Error)` - Failed to remove the user from the organization
253///
254/// # Important
255///
256/// This function only removes the membership relationship. It does not perform any
257/// cleanup of resources owned by or associated with the user within the organization.
258/// Additional logic may be needed to handle resource ownership transfer or deletion.
259///
260/// # Transaction Handling
261///
262/// This function uses a database transaction to ensure atomicity of the operation.
263/// If any part of the operation fails, the entire operation is rolled back.
264pub async fn remove_org_member(
265    pool: &Pool<MySql>,
266    org_id: i64,
267    user_id: i64,
268) -> anyhow::Result<()> {
269    let mut tx = pool.begin().await?;
270
271    sqlx::query("DELETE FROM orgmember WHERE org_id = ? AND user_id = ?")
272        .bind(org_id)
273        .bind(user_id)
274        .execute(&mut *tx)
275        .await
276        .context("Failed to remove organization member")?;
277
278    tx.commit().await?;
279    Ok(())
280}
281
282/// Updates a user's role within an organization.
283///
284/// This function modifies the role assigned to a user within an organization,
285/// which typically affects their permissions and access level. This is useful
286/// for promoting or demoting users within the organization's hierarchy.
287///
288/// # Arguments
289///
290/// * `pool` - Database connection pool for executing the query
291/// * `org_id` - Unique identifier of the organization
292/// * `user_id` - Unique identifier of the user whose role to update
293/// * `role` - New role to assign to the user (e.g., "admin", "member")
294///
295/// # Returns
296///
297/// * `Ok(())` - Successfully updated the user's role
298/// * `Err(anyhow::Error)` - Failed to update the user's role
299///
300/// # Error Handling
301///
302/// Returns an error if the user is not a member of the organization or if a database
303/// error occurs during the update operation.
304///
305/// # Transaction Handling
306///
307/// This function uses a database transaction to ensure atomicity of the operation.
308/// If any part of the operation fails, the entire operation is rolled back.
309///
310/// # Authorization Considerations
311///
312/// This function only performs the database operation to update a role. It does not
313/// implement any authorization logic to determine if the requesting user has permission
314/// to change roles. Such checks should be implemented in the business logic layer.
315pub async fn update_org_member_role(
316    pool: &Pool<MySql>,
317    org_id: i64,
318    user_id: i64,
319    role: &str,
320) -> anyhow::Result<()> {
321    let mut tx = pool.begin().await?;
322
323    sqlx::query("UPDATE orgmember SET role = ? WHERE org_id = ? AND user_id = ?")
324        .bind(role)
325        .bind(org_id)
326        .bind(user_id)
327        .execute(&mut *tx)
328        .await
329        .context("Failed to update organization member role")?;
330
331    tx.commit().await?;
332    Ok(())
333}