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

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