diff --git a/lib/crates/fabro-server/src/server/handler/runs.rs b/lib/crates/fabro-server/src/server/handler/runs.rs index 9f31597ad..b0596c4a8 100644 --- a/lib/crates/fabro-server/src/server/handler/runs.rs +++ b/lib/crates/fabro-server/src/server/handler/runs.rs @@ -125,19 +125,12 @@ pub(crate) fn board_columns() -> Vec { ] } -async fn board_run_metadata( - state: &AppState, - run_id: RunId, +fn board_run_metadata_from_projection( + projection: &fabro_store::RunProjection, ) -> serde_json::Map { let mut metadata = serde_json::Map::new(); - let Ok(run_store) = state.store.open_run_reader(&run_id).await else { - return metadata; - }; - let Ok(run_state) = run_store.state().await else { - return metadata; - }; - if let Some(pull_request) = run_state.pull_request { + if let Some(pull_request) = projection.pull_request.as_ref() { metadata.insert( "pull_request".to_string(), serde_json::json!({ @@ -146,13 +139,13 @@ async fn board_run_metadata( ); } - if let Some(sandbox) = run_state.sandbox { + if let Some(sandbox) = projection.sandbox.as_ref() { let mut sandbox_metadata = serde_json::Map::new(); sandbox_metadata.insert( "working_directory".to_string(), - serde_json::json!(sandbox.working_directory), + serde_json::json!(&sandbox.working_directory), ); - if let Some(identifier) = sandbox.identifier { + if let Some(identifier) = sandbox.identifier.as_ref() { sandbox_metadata.insert("id".to_string(), serde_json::json!(identifier)); } metadata.insert( @@ -162,7 +155,7 @@ async fn board_run_metadata( } if let Some((_, record)) = - run_state + projection .pending_interviews .iter() .min_by(|(left_id, left), (right_id, right)| { @@ -174,7 +167,7 @@ async fn board_run_metadata( metadata.insert( "question".to_string(), serde_json::json!({ - "text": record.question.text, + "text": &record.question.text, }), ); } @@ -196,9 +189,9 @@ async fn list_board_runs( State(state): State>, Query(pagination): Query, ) -> Response { - let summaries = match state + let entries = match state .store - .list_runs(&fabro_store::ListRunsQuery::default()) + .list_runs_with_projection(&fabro_store::ListRunsQuery::default()) .await { Ok(runs) => runs, @@ -207,23 +200,22 @@ async fn list_board_runs( .into_response(); } }; - let board_summaries: Vec<_> = summaries + let board_summaries: Vec<_> = entries .into_iter() - .filter_map(|summary| { + .filter_map(|(summary, projection)| { let column = board_column(summary.status)?; - Some((summary, column)) + Some((summary, projection, column)) }) .collect(); let (page_summaries, has_more) = paginate_items(board_summaries, &pagination); let mut data = Vec::with_capacity(page_summaries.len()); - for (summary, column) in page_summaries { - let run_id = summary.run_id; + for (summary, projection, column) in page_summaries { let mut item = serde_json::to_value(&summary).expect("RunSummary serialization is infallible"); item["column"] = serde_json::json!(column); if let Some(object) = item.as_object_mut() { - object.extend(board_run_metadata(state.as_ref(), run_id).await); + object.extend(board_run_metadata_from_projection(&projection)); } data.push(item); } diff --git a/lib/crates/fabro-store/src/slate/mod.rs b/lib/crates/fabro-store/src/slate/mod.rs index 947788b40..b9d21acf9 100644 --- a/lib/crates/fabro-store/src/slate/mod.rs +++ b/lib/crates/fabro-store/src/slate/mod.rs @@ -21,7 +21,7 @@ use slatedb::config::{CompressionCodec, Settings}; use tokio::sync::{Mutex, OnceCell}; use crate::run_state::build_summary; -use crate::{Error, ListRunsQuery, Result, keys}; +use crate::{Error, ListRunsQuery, Result, RunProjection, keys}; #[derive(Clone)] pub struct Database { @@ -190,6 +190,28 @@ impl Database { Ok(summaries) } + pub async fn list_runs_with_projection( + &self, + query: &ListRunsQuery, + ) -> Result> { + let db = self.open_db().await?; + let run_ids = self.catalog_index().await?.list(query).await?; + let mut entries = Vec::new(); + for run_id in run_ids { + if let Some(active) = self.get_active_run(&run_id).await { + let state = active.state().await?; + entries.push((build_summary(&state, &run_id), state)); + continue; + } + if !RunDatabase::has_any_events(&db, &run_id).await? { + continue; + } + entries.push(RunDatabase::build_summary_with_projection(&db, &run_id).await?); + } + entries.sort_by_key(|(summary, _)| std::cmp::Reverse(summary.run_id.created_at())); + Ok(entries) + } + pub async fn delete_run(&self, run_id: &RunId) -> Result<()> { let active = self.remove_active_run(run_id).await; if let Some(active) = &active { diff --git a/lib/crates/fabro-store/src/slate/run_store.rs b/lib/crates/fabro-store/src/slate/run_store.rs index f28e52341..83fd86250 100644 --- a/lib/crates/fabro-store/src/slate/run_store.rs +++ b/lib/crates/fabro-store/src/slate/run_store.rs @@ -118,12 +118,24 @@ impl RunDatabase { } pub(crate) async fn build_summary(db: &R, run_id: &RunId) -> Result + where + R: DbRead + Sync, + { + let (summary, _) = Self::build_summary_with_projection(db, run_id).await?; + Ok(summary) + } + + pub(crate) async fn build_summary_with_projection( + db: &R, + run_id: &RunId, + ) -> Result<(RunSummary, RunProjection)> where R: DbRead + Sync, { let events = list_events_from(db, run_id, 1).await?; let state = RunProjection::apply_events(&events)?; - Ok(build_summary(&state, run_id)) + let summary = build_summary(&state, run_id); + Ok((summary, state)) } async fn projected_state(&self) -> Result {