omni_orchestrator/schemas/v1/db/queries/build.rs
1use anyhow::Context;
2use sqlx::{FromRow, MySql, Pool};
3
4use libomni::types::db::v1 as types;
5use types::build::Build;
6
7/// Retrieves a paginated list of all builds in the system.
8///
9/// This function fetches builds with pagination support, ordering them by ID
10/// in ascending order (oldest first). It provides a way to browse through
11/// potentially large numbers of build records without loading them all at once.
12///
13/// # Arguments
14///
15/// * `pool` - Database connection pool for executing the query
16/// * `per_page` - Number of builds to return per page
17/// * `page` - Zero-based page number (e.g., 0 for first page, 1 for second page)
18///
19/// # Returns
20///
21/// * `Ok(Vec<Build>)` - Successfully retrieved list of builds for the requested page
22/// * `Err(anyhow::Error)` - Failed to fetch builds
23///
24/// # Pagination
25///
26/// The function calculates the appropriate OFFSET as `page * per_page`.
27/// For example, with per_page = 10:
28/// - page 0 → entries 0-9
29/// - page 1 → entries 10-19
30/// - page 2 → entries 20-29
31pub async fn list_builds_paginated(
32 pool: &Pool<MySql>,
33 per_page: i64,
34 page: i64,
35) -> anyhow::Result<Vec<Build>> {
36 let builds =
37 sqlx::query_as::<_, Build>("SELECT * FROM builds ORDER BY id ASC LIMIT ? OFFSET ?")
38 .bind(per_page)
39 .bind(page)
40 .fetch_all(pool)
41 .await
42 .context("Failed to fetch builds")?;
43
44 Ok(builds)
45}
46
47/// Retrieves the total number of builds in the system.
48pub async fn get_total_build_count(pool: &Pool<MySql>) -> anyhow::Result<i64> {
49 let count = sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM builds")
50 .fetch_one(pool)
51 .await
52 .context("Failed to fetch build count")?;
53
54 Ok(count)
55}
56
57
58/// Retrieves a paginated list of builds for a specific application.
59///
60/// This function fetches builds associated with a particular application,
61/// with pagination support. Results are ordered by build ID in ascending order
62/// (oldest first). This is useful for viewing the build history of a specific app.
63///
64/// # Arguments
65///
66/// * `pool` - Database connection pool for executing the query
67/// * `app_id` - Unique identifier of the application whose builds to retrieve
68/// * `per_page` - Number of builds to return per page
69/// * `offset` - Number of builds to skip (for pagination)
70///
71/// # Returns
72///
73/// * `Ok(Vec<Build>)` - Successfully retrieved list of builds for the application
74/// * `Err(anyhow::Error)` - Failed to fetch builds
75///
76/// # Note
77///
78/// Unlike `list_builds_paginated`, this function uses a direct offset value
79/// rather than calculating it from a page number. The caller must calculate
80/// the appropriate offset based on their pagination scheme (typically `page * per_page`).
81pub async fn list_builds_for_app_paginated(
82 pool: &Pool<MySql>,
83 app_id: i64,
84 per_page: i64,
85 offset: i64,
86) -> anyhow::Result<Vec<Build>> {
87 let builds = sqlx::query_as::<_, Build>(
88 "SELECT * FROM builds WHERE app_id = ? ORDER BY id ASC LIMIT ? OFFSET ?",
89 )
90 .bind(app_id)
91 .bind(per_page)
92 .bind(offset)
93 .fetch_all(pool)
94 .await
95 .context("Failed to fetch builds")?;
96
97 Ok(builds)
98}
99
100/// Retrieves a specific build by its unique identifier.
101///
102/// This function fetches detailed information about a single build record.
103/// It's typically used when specific build details are needed, such as
104/// viewing build logs or checking build status.
105///
106/// # Arguments
107///
108/// * `pool` - Database connection pool for executing the query
109/// * `id` - Unique identifier of the build to retrieve
110///
111/// # Returns
112///
113/// * `Ok(Build)` - Successfully retrieved build information
114/// * `Err(anyhow::Error)` - Failed to fetch build (including if not found)
115///
116/// # Error Handling
117///
118/// Returns an error if no build with the given ID exists or if a database
119/// error occurs during the query execution.
120pub async fn get_build_by_id(pool: &Pool<MySql>, id: i64) -> anyhow::Result<Build> {
121 let build = sqlx::query_as::<_, Build>("SELECT * FROM builds WHERE id = ?")
122 .bind(id)
123 .fetch_one(pool)
124 .await
125 .context("Failed to fetch build")?;
126
127 Ok(build)
128}
129
130/// Creates a new build record in the database.
131///
132/// This function inserts a new build entry with the provided parameters.
133/// It's typically called when a new build process is initiated for an application.
134///
135/// # Arguments
136///
137/// * `pool` - Database connection pool for executing the query
138/// * `app_id` - Identifier of the application this build belongs to
139/// * `git_commit` - Git commit hash or identifier for this build
140/// * `git_branch` - Git branch name used for this build
141/// * `git_repo` - Git repository URL or identifier
142/// * `status` - Initial status of the build (e.g., "pending", "in_progress")
143/// * `build_log` - Initial build log content (may be empty or contain setup information)
144///
145/// # Returns
146///
147/// * `Ok(Vec<Build>)` - Successfully created build record(s)
148/// * `Err(anyhow::Error)` - Failed to create build record
149///
150/// # Note
151///
152/// The function returns a vector of builds, which is unusual for a creation operation
153/// that typically returns a single record. This may be due to specific implementation
154/// requirements or to accommodate batch creation scenarios.
155///
156/// # Important
157///
158/// This function doesn't take a transaction parameter, so it commits changes
159/// immediately. For operations that need to be part of a larger transaction,
160/// consider enhancing this function to accept a transaction parameter.
161pub async fn create_build(
162 pool: &Pool<MySql>,
163 app_id: i64,
164 git_commit: &str,
165 git_branch: &str,
166 git_repo: &str,
167 status: &str,
168 build_log: &str,
169) -> anyhow::Result<Vec<Build>> {
170 let builds = sqlx::query_as::<_, Build>(
171 "INSERT INTO builds (app_id, git_commit, git_branch, git_repo, status, build_log) VALUES (?, ?, ?, ?, ?, ?)"
172 )
173 .bind(app_id)
174 .bind(git_commit)
175 .bind(git_branch)
176 .bind(git_repo)
177 .bind(status)
178 .bind(build_log)
179 .fetch_all(pool)
180 .await
181 .context("Failed to update build")?;
182
183 Ok(builds)
184}
185
186/// Updates an existing build record with new status and log information.
187///
188/// This function modifies a build record to reflect the current state of the
189/// build process. It's typically called during or after a build process to
190/// update its status and append to the build log.
191///
192/// # Arguments
193///
194/// * `pool` - Database connection pool for executing the query
195/// * `id` - Unique identifier of the build to update
196/// * `status` - New status of the build (e.g., "success", "failed", "in_progress")
197/// * `build_log` - Updated build log content
198///
199/// # Returns
200///
201/// * `Ok(Build)` - Successfully updated build record
202/// * `Err(anyhow::Error)` - Failed to update build
203///
204/// # Use Cases
205///
206/// Common use cases for this function include:
207/// - Updating build status as it progresses through different stages
208/// - Appending build output to the log as it becomes available
209/// - Marking a build as complete with its final status
210///
211/// # Note
212///
213/// This function replaces the entire build log content rather than appending to it.
214/// If incremental updates are needed, the caller should fetch the current log,
215/// append to it, and then pass the complete updated log to this function.
216pub async fn update_build(
217 pool: &Pool<MySql>,
218 id: i64,
219 status: &str,
220 build_log: &str,
221) -> anyhow::Result<Build> {
222 let build =
223 sqlx::query_as::<_, Build>("UPDATE builds SET status = ?, build_log = ? WHERE id = ?")
224 .bind(status)
225 .bind(build_log)
226 .bind(id)
227 .fetch_one(pool)
228 .await
229 .context("Failed to update build")?;
230
231 Ok(build)
232}
233
234/// Deletes a specific build record from the database.
235///
236/// This function permanently removes a build record identified by its ID.
237/// It's typically used for cleanup operations or when a build was created erroneously.
238///
239/// # Arguments
240///
241/// * `pool` - Database connection pool for executing the query
242/// * `id` - Unique identifier of the build to delete
243///
244/// # Returns
245///
246/// * `Ok(())` - Successfully deleted the build
247/// * `Err(anyhow::Error)` - Failed to delete the build
248///
249/// # Warning
250///
251/// This operation is irreversible. Once a build is deleted, all associated
252/// information including build logs and status history is permanently lost.
253///
254/// # Note
255///
256/// This function does not verify if the build exists before attempting deletion.
257/// If the build does not exist, the operation will still succeed (as far as SQL is concerned),
258/// but no rows will be affected.
259pub async fn delete_build(pool: &Pool<MySql>, id: i64) -> anyhow::Result<()> {
260 sqlx::query("DELETE FROM builds WHERE id = ?")
261 .bind(id)
262 .execute(pool)
263 .await
264 .context("Failed to delete build")?;
265
266 Ok(())
267}
268
269/// Deletes all build records associated with a specific application.
270///
271/// This function permanently removes all build records for a given application.
272/// It's typically used when an application is being deleted, or when a complete
273/// build history reset is desired.
274///
275/// # Arguments
276///
277/// * `pool` - Database connection pool for executing the query
278/// * `app_id` - Unique identifier of the application whose builds should be deleted
279///
280/// # Returns
281///
282/// * `Ok(())` - Successfully deleted the builds for the application
283/// * `Err(anyhow::Error)` - Failed to delete the builds
284///
285/// # Warning
286///
287/// This operation is irreversible and bulk in nature. It will delete all build
288/// records for the specified application without any additional confirmation.
289/// Use with caution, especially in production environments.
290///
291/// # Use Cases
292///
293/// Common scenarios for using this function include:
294/// - Application deletion (cleanup of associated data)
295/// - Build history purging for storage optimization
296/// - Resetting an application's build history before migration or major changes
297///
298/// # Note
299///
300/// If the application has no builds, this operation will succeed but affect zero rows.
301pub async fn delete_builds_for_app(pool: &Pool<MySql>, app_id: i64) -> anyhow::Result<()> {
302 sqlx::query("DELETE FROM builds WHERE app_id = ?")
303 .bind(app_id)
304 .execute(pool)
305 .await
306 .context("Failed to delete builds for app")?;
307
308 Ok(())
309}