loops: iteration timeline — GET /api/topology-runs?loop_id=X
Extend the topology-runs list route with an optional loop_id filter that returns iterations for a single loop, newest-iteration-first. Adds the iteration and finished_at columns to the summary (skip-null on the JSON so compares stay compact). Backed by list_by_loop in the repo, which uses the existing topology_runs_loop_idx partial index. LoopsCanvas fetches the runs in parallel with the loop detail and renders an iteration timeline card (iteration #, status pill, start time, duration, run id prefix) between the graph section and the actions row.
This commit is contained in:
@@ -10,13 +10,17 @@ use uuid::Uuid;
|
||||
|
||||
use crate::DbError;
|
||||
|
||||
/// A row summary for the recent-runs list.
|
||||
/// A row summary for the recent-runs list. `iteration` and `finished_at`
|
||||
/// are populated for loop iterations and for terminal runs respectively;
|
||||
/// `None` for compares or still-in-flight runs.
|
||||
pub struct TopologyRunSummary {
|
||||
pub id: Uuid,
|
||||
pub task: String,
|
||||
pub status: String,
|
||||
pub kind: String,
|
||||
pub created_at: OffsetDateTime,
|
||||
pub iteration: Option<i32>,
|
||||
pub finished_at: Option<OffsetDateTime>,
|
||||
}
|
||||
|
||||
/// A full saved comparison run.
|
||||
@@ -271,7 +275,8 @@ pub async fn list_recent(
|
||||
limit: i64,
|
||||
) -> Result<Vec<TopologyRunSummary>, DbError> {
|
||||
let rows = sqlx::query!(
|
||||
"SELECT id, task, status, kind, created_at FROM topology_runs
|
||||
"SELECT id, task, status, kind, created_at, iteration, finished_at
|
||||
FROM topology_runs
|
||||
WHERE workspace_id = $1 ORDER BY created_at DESC LIMIT $2",
|
||||
workspace_id.as_uuid(),
|
||||
limit,
|
||||
@@ -286,6 +291,42 @@ pub async fn list_recent(
|
||||
status: r.status,
|
||||
kind: r.kind,
|
||||
created_at: r.created_at,
|
||||
iteration: r.iteration,
|
||||
finished_at: r.finished_at,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Iterations of a loop, newest first. Uses the partial index
|
||||
/// `topology_runs_loop_idx` on `(loop_id, iteration DESC)`.
|
||||
pub async fn list_by_loop(
|
||||
pool: &PgPool,
|
||||
workspace_id: WorkspaceId,
|
||||
loop_id: Uuid,
|
||||
limit: i64,
|
||||
) -> Result<Vec<TopologyRunSummary>, DbError> {
|
||||
let rows = sqlx::query!(
|
||||
"SELECT id, task, status, kind, created_at, iteration, finished_at
|
||||
FROM topology_runs
|
||||
WHERE workspace_id = $1 AND loop_id = $2
|
||||
ORDER BY iteration DESC NULLS LAST, created_at DESC
|
||||
LIMIT $3",
|
||||
workspace_id.as_uuid(),
|
||||
loop_id,
|
||||
limit,
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
Ok(rows
|
||||
.into_iter()
|
||||
.map(|r| TopologyRunSummary {
|
||||
id: r.id,
|
||||
task: r.task,
|
||||
status: r.status,
|
||||
kind: r.kind,
|
||||
created_at: r.created_at,
|
||||
iteration: r.iteration,
|
||||
finished_at: r.finished_at,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user