diff --git a/databuild/cli/main.rs b/databuild/cli/main.rs index 9c01ba1..4f2ba7b 100644 --- a/databuild/cli/main.rs +++ b/databuild/cli/main.rs @@ -900,7 +900,7 @@ async fn handle_builds_command(matches: &ArgMatches, event_log_uri: &str) -> Res } _ => { println!("Build: {}", detail.build_request_id); - println!("Status: {} ({})", detail.status_name, detail.status_code); + println!("Status: {} ({})", detail.status.clone().unwrap().name, detail.status.unwrap().code); println!("Requested partitions: {}", detail.requested_partitions.len()); println!("Total jobs: {}", detail.total_jobs); println!("Completed jobs: {}", detail.completed_jobs); @@ -958,11 +958,7 @@ async fn handle_builds_command(matches: &ArgMatches, event_log_uri: &str) -> Res println!("\nTimeline ({} events):", detail.timeline.len()); for event in detail.timeline { let timestamp = format_timestamp(event.timestamp); - let status_info = if let Some(ref status_name) = event.status_name { - format!(" -> {}", status_name) - } else { - String::new() - }; + let status_info = event.status.unwrap().name; println!(" {} [{}]{} {}", timestamp, event.event_type, status_info, event.message); if let Some(ref reason) = event.cancel_reason { diff --git a/databuild/client/BUILD.bazel b/databuild/client/BUILD.bazel index c4d8fe8..e9006f8 100644 --- a/databuild/client/BUILD.bazel +++ b/databuild/client/BUILD.bazel @@ -44,6 +44,7 @@ genrule( "typescript_generated/src/models/BuildRequest.ts", "typescript_generated/src/models/BuildRequestResponse.ts", "typescript_generated/src/models/BuildSummary.ts", + "typescript_generated/src/models/BuildRequestStatus.ts", "typescript_generated/src/models/BuildTimelineEvent.ts", "typescript_generated/src/models/BuildsListApiResponse.ts", "typescript_generated/src/models/BuildsListResponse.ts", @@ -118,6 +119,7 @@ genrule( cp $$TEMP_DIR/src/models/BuildRequest.ts $(location typescript_generated/src/models/BuildRequest.ts) cp $$TEMP_DIR/src/models/BuildRequestResponse.ts $(location typescript_generated/src/models/BuildRequestResponse.ts) cp $$TEMP_DIR/src/models/BuildSummary.ts $(location typescript_generated/src/models/BuildSummary.ts) + cp $$TEMP_DIR/src/models/BuildRequestStatus.ts $(location typescript_generated/src/models/BuildRequestStatus.ts) cp $$TEMP_DIR/src/models/BuildTimelineEvent.ts $(location typescript_generated/src/models/BuildTimelineEvent.ts) cp $$TEMP_DIR/src/models/BuildsListApiResponse.ts $(location typescript_generated/src/models/BuildsListApiResponse.ts) cp $$TEMP_DIR/src/models/BuildsListResponse.ts $(location typescript_generated/src/models/BuildsListResponse.ts) diff --git a/databuild/dashboard/pages.ts b/databuild/dashboard/pages.ts index 6a8ea4d..b1c9b68 100644 --- a/databuild/dashboard/pages.ts +++ b/databuild/dashboard/pages.ts @@ -220,8 +220,8 @@ export const RecentActivity: TypedComponent = { }, build.build_request_id) ]), m('td', [ - // KEY FIX: build.status_name is now always a string, prevents runtime errors - m(BuildStatusBadge, { status: build.status_name }) + // KEY FIX: build.status.name is now always a string, prevents runtime errors + m(BuildStatusBadge, { status: build.status.name }) ]), m('td.text-sm.opacity-70', formatTime(build.requested_at)), ]) @@ -276,7 +276,7 @@ export const RecentActivity: TypedComponent = { }, partition.partition_ref.str) ]), m('td', [ - // KEY FIX: partition.status_name is now always a string, prevents runtime errors + // KEY FIX: partition.status.name is now always a string, prevents runtime errors m(PartitionStatusBadge, { status: partition.status_name }) ]), m('td.text-sm.opacity-70', @@ -402,8 +402,8 @@ export const BuildStatus: TypedComponent = { startPolling() { // Use different poll intervals based on build status - const isActive = this.data?.status_name === 'EXECUTING' || - this.data?.status_name === 'PLANNING'; + const isActive = this.data?.status.name === 'EXECUTING' || + this.data?.status.name === 'PLANNING'; const interval = isActive ? 2000 : 10000; // 2s for active, 10s for completed pollingManager.startPolling(`build-status-${this.buildId}`, () => { @@ -457,7 +457,7 @@ export const BuildStatus: TypedComponent = { {stage: 'Build Requested', time: build.requested_at, icon: '🕚'}, ...(build.started_at ? [{stage: 'Build Started', time: build.started_at, icon: '▶️'}] : []), // ...(this.data.events as BuildEvent[]).filter(ev => ev.job_event !== null).map((ev) => ({ - // stage: ev.job_event.status_name, time: ev.timestamp, icon: '🙃' + // stage: ev.job_event.status.name, time: ev.timestamp, icon: '🙃' // })), ...(build.completed_at ? [{stage: 'Build Completed', time: build.completed_at, icon: '✅'}] : []), ]; @@ -472,7 +472,7 @@ export const BuildStatus: TypedComponent = { m('.stat.bg-base-100.shadow.rounded-lg.p-4', [ m('.stat-title', 'Status'), m('.stat-value.text-2xl', [ - m(BuildStatusBadge, { status: build.status_name, size: 'lg' }) + m(BuildStatusBadge, { status: build.status.name, size: 'lg' }) ]) ]), m('.stat.bg-base-100.shadow.rounded-lg.p-4', [ @@ -518,7 +518,7 @@ export const BuildStatus: TypedComponent = { m('.partition-status.flex.justify-between.items-center', [ // CLEAN: Always string status, no nested object access m(PartitionStatusBadge, { - status: partitionStatus?.status_name || 'Loading...', + status: partitionStatus?.status.name || 'Loading...', size: 'sm' }), partitionStatus?.last_updated ? @@ -905,15 +905,15 @@ export const PartitionStatus: TypedComponent = { // Update status based on event type if (event.eventType === 'build_request') { if (event.message?.includes('completed') || event.message?.includes('successful')) { - build.status_name = 'Completed'; + build.status.name = 'Completed'; build.completedAt = event.timestamp; } else if (event.message?.includes('failed') || event.message?.includes('error')) { - build.status_name = 'Failed'; + build.status.name = 'Failed'; build.completedAt = event.timestamp; } else if (event.message?.includes('executing') || event.message?.includes('running')) { - build.status_name = 'Executing'; + build.status.name = 'Executing'; } else if (event.message?.includes('planning')) { - build.status_name = 'Planning'; + build.status.name = 'Planning'; } } } @@ -1012,7 +1012,7 @@ export const PartitionStatus: TypedComponent = { ]), m('div.partition-meta.flex.gap-4.items-center.mb-4', [ - m(PartitionStatusBadge, { status: this.data?.status_name || 'Unknown', size: 'lg' }), + m(PartitionStatusBadge, { status: this.data?.status.name || 'Unknown', size: 'lg' }), this.data?.last_updated ? m('.timestamp.text-sm.opacity-70', `Last updated: ${formatDateTime(this.data.last_updated)}`) : null, @@ -1051,7 +1051,7 @@ export const PartitionStatus: TypedComponent = { }, build.id) ]), m('td', [ - m(BuildStatusBadge, { status: build.status_name }) + m(BuildStatusBadge, { status: build.status.name }) ]), m('td.text-sm.opacity-70', formatDateTime(build.startedAt)), diff --git a/databuild/dashboard/services.ts b/databuild/dashboard/services.ts index 11dd8a6..62de35b 100644 --- a/databuild/dashboard/services.ts +++ b/databuild/dashboard/services.ts @@ -38,8 +38,7 @@ const apiClient = new DefaultApi(apiConfig); function transformBuildSummary(apiResponse: BuildSummary): DashboardBuild { return { build_request_id: apiResponse.build_request_id, - status_code: apiResponse.status_code, - status_name: apiResponse.status_name, + status: apiResponse.status!, requested_partitions: apiResponse.requested_partitions, // Keep as PartitionRef array total_jobs: apiResponse.total_jobs, completed_jobs: apiResponse.completed_jobs, @@ -56,8 +55,7 @@ function transformBuildSummary(apiResponse: BuildSummary): DashboardBuild { function transformBuildDetail(apiResponse: BuildDetailResponse): DashboardBuild { return { build_request_id: apiResponse.build_request_id, - status_code: apiResponse.status_code, - status_name: apiResponse.status_name, + status: apiResponse.status!, requested_partitions: apiResponse.requested_partitions, // Keep as PartitionRef array total_jobs: apiResponse.total_jobs, completed_jobs: apiResponse.completed_jobs, diff --git a/databuild/dashboard/transformation-tests.ts b/databuild/dashboard/transformation-tests.ts index e6ca073..99611db 100644 --- a/databuild/dashboard/transformation-tests.ts +++ b/databuild/dashboard/transformation-tests.ts @@ -7,7 +7,8 @@ import { BuildDetailResponse, PartitionSummary, JobSummary, - ActivityResponse + ActivityResponse, + BuildRequestStatus } from '../client/typescript_generated/src/index'; // Import types directly since we're now in the same ts_project @@ -26,8 +27,7 @@ import { function transformBuildSummary(apiResponse: BuildSummary): DashboardBuild { return { build_request_id: apiResponse.build_request_id, - status_code: apiResponse.status_code, - status_name: apiResponse.status_name, + status: apiResponse.status!, requested_partitions: apiResponse.requested_partitions, // Keep as PartitionRef array total_jobs: apiResponse.total_jobs, completed_jobs: apiResponse.completed_jobs, @@ -44,8 +44,7 @@ function transformBuildSummary(apiResponse: BuildSummary): DashboardBuild { function transformBuildDetail(apiResponse: BuildDetailResponse): DashboardBuild { return { build_request_id: apiResponse.build_request_id, - status_code: apiResponse.status_code, - status_name: apiResponse.status_name, + status: apiResponse.status!, requested_partitions: apiResponse.requested_partitions, // Keep as PartitionRef array total_jobs: apiResponse.total_jobs, completed_jobs: apiResponse.completed_jobs, @@ -98,8 +97,7 @@ function transformActivityResponse(apiResponse: ActivityResponse): DashboardActi // Test Data Mocks const mockBuildSummary: BuildSummary = { build_request_id: 'build-123', - status_code: 4, // BUILD_REQUEST_COMPLETED - status_name: 'COMPLETED', + status: {code: 4, name: 'COMPLETED'}, requested_partitions: [{ str: 'partition-1' }, { str: 'partition-2' }], total_jobs: 5, completed_jobs: 5, @@ -151,12 +149,12 @@ o.spec('Transformation Functions', () => { const result = transformBuildSummary(mockBuildSummary); // The key fix: status_name should be a string, status_code a number - o(typeof result.status_code).equals('number'); - o(typeof result.status_name).equals('string'); - o(result.status_name).equals('COMPLETED'); + o(typeof result.status?.code).equals('number'); + o(typeof result.status?.name).equals('string'); + o(result.status.name).equals('COMPLETED'); // This should not throw (preventing the original runtime error) - o(() => result.status_name.toLowerCase()).notThrows('status_name.toLowerCase should work'); + o(() => result.status.name.toLowerCase()).notThrows('status_name.toLowerCase should work'); }); o('transformBuildSummary handles null optional fields', () => { @@ -219,7 +217,7 @@ o.spec('Transformation Functions', () => { // All nested objects should be properly transformed o(result.recent_builds.length).equals(1); - o(typeof result.recent_builds[0]?.status_name).equals('string'); + o(typeof result.recent_builds[0]?.status.name).equals('string'); o(result.recent_partitions.length).equals(1); o(typeof result.recent_partitions[0]?.partition_ref).equals('object'); @@ -233,8 +231,8 @@ o.spec('Transformation Functions', () => { // 1. status_name.toLowerCase() - should not crash result.recent_builds.forEach((build: DashboardBuild) => { - o(() => build.status_name.toLowerCase()).notThrows('build.status_name.toLowerCase should work'); - o(build.status_name.toLowerCase()).equals('completed'); + o(() => build.status.name.toLowerCase()).notThrows('build.status.name.toLowerCase should work'); + o(build.status.name.toLowerCase()).equals('completed'); }); // 2. partition_ref.str access - should access string property diff --git a/databuild/dashboard/types.ts b/databuild/dashboard/types.ts index 5f3f1af..8dc82c3 100644 --- a/databuild/dashboard/types.ts +++ b/databuild/dashboard/types.ts @@ -11,7 +11,8 @@ import { JobMetricsResponse, JobDailyStats, JobRunSummary, - PartitionRef + PartitionRef, + BuildRequestStatus } from '../client/typescript_generated/src/index'; // Dashboard-optimized types - canonical frontend types independent of backend schema @@ -19,8 +20,7 @@ import { export interface DashboardBuild { build_request_id: string; - status_code: number; - status_name: string; + status: BuildRequestStatus; requested_partitions: PartitionRef[]; total_jobs: number; completed_jobs: number; @@ -66,8 +66,7 @@ export interface DashboardActivity { // Dashboard timeline event types for consistent UI handling export interface DashboardBuildTimelineEvent { timestamp: number; - status_code: number; - status_name: string; + status: BuildRequestStatus; message: string; event_type: string; cancel_reason?: string; @@ -75,8 +74,7 @@ export interface DashboardBuildTimelineEvent { export interface DashboardPartitionTimelineEvent { timestamp: number; - status_code: number; - status_name: string; + status: BuildRequestStatus; message: string; build_request_id: string; job_run_id?: string; @@ -249,8 +247,8 @@ export function isDashboardActivity(data: any): data is DashboardActivity { export function isDashboardBuild(data: any): data is DashboardBuild { return data && typeof data.build_request_id === 'string' && - typeof data.status_code === 'number' && - typeof data.status_name === 'string' && + typeof data.status?.code === 'number' && + typeof data.status?.name === 'string' && typeof data.requested_at === 'number' && Array.isArray(data.requested_partitions); } diff --git a/databuild/databuild.proto b/databuild/databuild.proto index 947643c..57e3b34 100644 --- a/databuild/databuild.proto +++ b/databuild/databuild.proto @@ -239,9 +239,9 @@ message BuildRequestEvent { // Optional status message string message = 4; // The comment attached to the request - contains arbitrary text - string comment = 5; + optional string comment = 5; // The id of the want that triggered this build - string want_id = 6; + optional string want_id = 6; } // Partition state change event @@ -301,6 +301,7 @@ message WantEvent { string want_id = 2; // How this want was created WantSource source = 3; + string comment = 4; } message PartitionWant { @@ -362,6 +363,7 @@ message BuildEvent { // Event metadata string event_id = 1; // UUID for this event int64 timestamp = 2; // Unix timestamp (nanoseconds) + optional string build_request_id = 3; // Event type and payload (one of) oneof event_type { @@ -530,19 +532,18 @@ message BuildsListResponse { message BuildSummary { string build_request_id = 1; - BuildRequestStatusCode status_code = 2; // Enum for programmatic use - string status_name = 3; // Human-readable string - repeated PartitionRef requested_partitions = 4; - uint32 total_jobs = 5; - uint32 completed_jobs = 6; - uint32 failed_jobs = 7; - uint32 cancelled_jobs = 8; - int64 requested_at = 9; - optional int64 started_at = 10; - optional int64 completed_at = 11; - optional int64 duration_ms = 12; - bool cancelled = 13; - string comment = 14; + BuildRequestStatus status = 2; + repeated PartitionRef requested_partitions = 3; + uint32 total_jobs = 4; + uint32 completed_jobs = 5; + uint32 failed_jobs = 6; + uint32 cancelled_jobs = 7; + int64 requested_at = 8; + optional int64 started_at = 9; + optional int64 completed_at = 10; + optional int64 duration_ms = 11; + bool cancelled = 12; + optional string comment = 13; } // @@ -572,29 +573,27 @@ message BuildDetailRequest { message BuildDetailResponse { string build_request_id = 1; - BuildRequestStatusCode status_code = 2; // Enum for programmatic use - string status_name = 3; // Human-readable string - repeated PartitionRef requested_partitions = 4; - uint32 total_jobs = 5; - uint32 completed_jobs = 6; - uint32 failed_jobs = 7; - uint32 cancelled_jobs = 8; - int64 requested_at = 9; - optional int64 started_at = 10; - optional int64 completed_at = 11; - optional int64 duration_ms = 12; - bool cancelled = 13; - optional string cancel_reason = 14; - repeated BuildTimelineEvent timeline = 15; + BuildRequestStatus status = 2; + repeated PartitionRef requested_partitions = 3; + uint32 total_jobs = 4; + uint32 completed_jobs = 5; + uint32 failed_jobs = 6; + uint32 cancelled_jobs = 7; + int64 requested_at = 8; + optional int64 started_at = 9; + optional int64 completed_at = 10; + optional int64 duration_ms = 11; + bool cancelled = 12; + optional string cancel_reason = 13; + repeated BuildTimelineEvent timeline = 14; } message BuildTimelineEvent { int64 timestamp = 1; - optional BuildRequestStatusCode status_code = 2; // Enum for programmatic use - optional string status_name = 3; // Human-readable string - string message = 4; - string event_type = 5; - optional string cancel_reason = 6; + optional BuildRequestStatus status = 2; + string message = 3; + string event_type = 4; + optional string cancel_reason = 5; } // diff --git a/databuild/event_log/mock.rs b/databuild/event_log/mock.rs index eb14248..99d3813 100644 --- a/databuild/event_log/mock.rs +++ b/databuild/event_log/mock.rs @@ -204,6 +204,8 @@ impl MockBuildEventLog { Some(crate::build_event::EventType::PartitionInvalidationEvent(_)) => "partition_invalidation", Some(crate::build_event::EventType::JobRunCancelEvent(_)) => "job_run_cancel", Some(crate::build_event::EventType::BuildCancelEvent(_)) => "build_cancel", + Some(crate::build_event::EventType::WantEvent(_)) => "want", + Some(crate::build_event::EventType::TaintEvent(_)) => "taint", None => "unknown", }, event_data @@ -220,7 +222,7 @@ impl MockBuildEventLog { "INSERT INTO build_request_events (event_id, status, requested_partitions, message) VALUES (?1, ?2, ?3, ?4)", rusqlite::params![ event.event_id, - br_event.status_code.to_string(), + br_event.clone().status.unwrap().code.to_string(), partitions_json, br_event.message ], @@ -379,12 +381,13 @@ pub mod test_events { BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id: build_request_id.unwrap_or_else(|| Uuid::new_v4().to_string()), + build_request_id, event_type: Some(build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: BuildRequestStatus::BuildRequestReceived as i32, - status_name: BuildRequestStatus::BuildRequestReceived.to_display_string(), + status: Some(BuildRequestStatusCode::BuildRequestReceived.status()), requested_partitions: partitions, message: "Build request received".to_string(), + comment: None, + want_id: None, })), } } @@ -398,12 +401,13 @@ pub mod test_events { BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id: build_request_id.unwrap_or_else(|| Uuid::new_v4().to_string()), + build_request_id, event_type: Some(build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: status as i32, - status_name: status.to_display_string(), + status: Some(status.clone()), requested_partitions: partitions, - message: format!("Build request status: {:?}", status), + message: format!("Build request status: {:?}", status.name), + comment: None, + want_id: None, })), } } @@ -418,7 +422,7 @@ pub mod test_events { BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id: build_request_id.unwrap_or_else(|| Uuid::new_v4().to_string()), + build_request_id, event_type: Some(build_event::EventType::PartitionEvent(PartitionEvent { partition_ref: Some(partition_ref), status_code: status as i32, @@ -440,7 +444,7 @@ pub mod test_events { BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id: build_request_id.unwrap_or_else(|| Uuid::new_v4().to_string()), + build_request_id, event_type: Some(build_event::EventType::JobEvent(JobEvent { job_run_id: job_run_id.unwrap_or_else(|| Uuid::new_v4().to_string()), job_label: Some(job_label), @@ -563,9 +567,10 @@ impl BELStorage for MockBELStorage { // Apply filtering based on EventFilter events.retain(|event| { + // Filter by build request IDs if specified if !filter.build_request_ids.is_empty() { - if !filter.build_request_ids.contains(&event.build_request_id) { + if !filter.build_request_ids.contains(&event.build_request_id.clone().unwrap()) { return false; } } diff --git a/databuild/event_log/mod.rs b/databuild/event_log/mod.rs index a7460e8..4e173cf 100644 --- a/databuild/event_log/mod.rs +++ b/databuild/event_log/mod.rs @@ -85,7 +85,7 @@ pub fn create_build_event( BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id, + build_request_id: Some(build_request_id.clone()), event_type: Some(event_type), } } diff --git a/databuild/event_log/query_engine.rs b/databuild/event_log/query_engine.rs index 0b86373..55733be 100644 --- a/databuild/event_log/query_engine.rs +++ b/databuild/event_log/query_engine.rs @@ -38,15 +38,16 @@ impl BELQueryEngine { }; let events = self.storage.list_events(0, filter).await?; - let mut active_builds = Vec::new(); - let mut build_states: HashMap = HashMap::new(); + let mut active_builds: Vec = Vec::new(); + let mut build_states: HashMap = HashMap::new(); // Process events chronologically to track build states for event in events.events { + let build_request_id = event.build_request_id.clone().unwrap(); match &event.event_type { Some(crate::build_event::EventType::BuildRequestEvent(br_event)) => { - if let Ok(status) = BuildRequestStatus::try_from(br_event.status_code) { - build_states.insert(event.build_request_id.clone(), status); + if let Ok(code) = BuildRequestStatusCode::try_from(br_event.clone().status.unwrap().code) { + build_states.insert(build_request_id.clone(), code); } } Some(crate::build_event::EventType::PartitionEvent(p_event)) => { @@ -56,15 +57,15 @@ impl BELQueryEngine { if let Ok(status) = PartitionStatus::try_from(p_event.status_code) { if matches!(status, PartitionStatus::PartitionBuilding | PartitionStatus::PartitionAnalyzed) { // Check if the build request is still active - if let Some(build_status) = build_states.get(&event.build_request_id) { + if let Some(build_status) = build_states.get(&build_request_id) { if matches!(build_status, - BuildRequestStatus::BuildRequestReceived | - BuildRequestStatus::BuildRequestPlanning | - BuildRequestStatus::BuildRequestExecuting | - BuildRequestStatus::BuildRequestAnalysisCompleted + BuildRequestStatusCode::BuildRequestReceived | + BuildRequestStatusCode::BuildRequestPlanning | + BuildRequestStatusCode::BuildRequestExecuting | + BuildRequestStatusCode::BuildRequestAnalysisCompleted ) { - if !active_builds.contains(&event.build_request_id) { - active_builds.push(event.build_request_id.clone()); + if !active_builds.contains(&build_request_id) { + active_builds.push(build_request_id.clone()); } } } @@ -97,12 +98,12 @@ impl BELQueryEngine { return Err(BuildEventLogError::QueryError(format!("Build request '{}' not found", build_id))); } - let mut status = BuildRequestStatus::BuildRequestUnknown; + let mut status = BuildRequestStatusCode::BuildRequestUnknown.status(); let mut requested_partitions = Vec::new(); let mut created_at = 0i64; let mut updated_at = 0i64; - for event in events.events { + for event in events.events.iter().filter(|event| event.build_request_id.is_some()) { if event.timestamp > 0 { if created_at == 0 || event.timestamp < created_at { created_at = event.timestamp; @@ -113,7 +114,7 @@ impl BELQueryEngine { } if let Some(crate::build_event::EventType::BuildRequestEvent(br_event)) = &event.event_type { - if let Ok(event_status) = BuildRequestStatus::try_from(br_event.status_code) { + if let Ok(event_status) = BuildRequestStatus::try_from(br_event.status.clone().unwrap()) { status = event_status; } if !br_event.requested_partitions.is_empty() { @@ -148,20 +149,20 @@ impl BELQueryEngine { let mut build_summaries: HashMap = HashMap::new(); // Aggregate by build request ID - for event in events.events { + for event in events.events.iter().filter(|event| event.build_request_id.is_some()) { if let Some(crate::build_event::EventType::BuildRequestEvent(br_event)) = &event.event_type { - let build_id = &event.build_request_id; + let build_id = &event.build_request_id.clone().unwrap(); let entry = build_summaries.entry(build_id.clone()).or_insert_with(|| { BuildRequestSummary { build_request_id: build_id.clone(), - status: BuildRequestStatus::BuildRequestUnknown, + status: BuildRequestStatusCode::BuildRequestUnknown.status(), requested_partitions: Vec::new(), created_at: event.timestamp, updated_at: event.timestamp, } }); - if let Ok(status) = BuildRequestStatus::try_from(br_event.status_code) { + if let Ok(status) = BuildRequestStatus::try_from(br_event.status.clone().unwrap()) { entry.status = status; } entry.updated_at = event.timestamp.max(entry.updated_at); @@ -179,8 +180,8 @@ impl BELQueryEngine { // Apply status filter if provided if let Some(status_filter) = &request.status_filter { if let Ok(filter_status) = status_filter.parse::() { - if let Ok(status) = BuildRequestStatus::try_from(filter_status) { - builds.retain(|b| b.status == status); + if let Ok(status) = BuildRequestStatusCode::try_from(filter_status) { + builds.retain(|b| b.status.code == status as i32); } } } @@ -194,8 +195,7 @@ impl BELQueryEngine { .take(limit) .map(|summary| BuildSummary { build_request_id: summary.build_request_id, - status_code: summary.status as i32, - status_name: summary.status.to_display_string(), + status: Some(summary.status), requested_partitions: summary.requested_partitions.into_iter() .map(|s| PartitionRef { str: s }) .collect(), @@ -208,6 +208,7 @@ impl BELQueryEngine { completed_at: None, // TODO: Implement duration_ms: None, // TODO: Implement cancelled: false, // TODO: Implement + comment: None, }) .collect(); @@ -228,18 +229,18 @@ impl BELQueryEngine { let active_builds_count = builds_response.builds.iter() .filter(|b| matches!( - BuildRequestStatus::try_from(b.status_code).unwrap_or(BuildRequestStatus::BuildRequestUnknown), - BuildRequestStatus::BuildRequestReceived | - BuildRequestStatus::BuildRequestPlanning | - BuildRequestStatus::BuildRequestExecuting | - BuildRequestStatus::BuildRequestAnalysisCompleted + BuildRequestStatusCode::try_from(b.status.clone().unwrap().code).unwrap_or(BuildRequestStatusCode::BuildRequestUnknown), + BuildRequestStatusCode::BuildRequestReceived | + BuildRequestStatusCode::BuildRequestPlanning | + BuildRequestStatusCode::BuildRequestExecuting | + BuildRequestStatusCode::BuildRequestAnalysisCompleted )) .count() as u32; let recent_builds = builds_response.builds.into_iter() .map(|b| BuildRequestSummary { build_request_id: b.build_request_id, - status: BuildRequestStatus::try_from(b.status_code).unwrap_or(BuildRequestStatus::BuildRequestUnknown), + status: b.status.unwrap_or(BuildRequestStatusCode::BuildRequestUnknown.status()), requested_partitions: b.requested_partitions.into_iter().map(|p| p.str).collect(), created_at: b.requested_at, updated_at: b.completed_at.unwrap_or(b.requested_at), @@ -299,7 +300,7 @@ impl BELQueryEngine { if partition_event_ref.str == partition_ref { if let Ok(status) = PartitionStatus::try_from(p_event.status_code) { if status == PartitionStatus::PartitionAvailable && event.timestamp >= latest_timestamp { - latest_available_build_id = Some(event.build_request_id.clone()); + latest_available_build_id = event.build_request_id.clone(); latest_timestamp = event.timestamp; } } diff --git a/databuild/event_log/writer.rs b/databuild/event_log/writer.rs index 7a718d3..329d305 100644 --- a/databuild/event_log/writer.rs +++ b/databuild/event_log/writer.rs @@ -35,10 +35,11 @@ impl EventWriter { let event = create_build_event( build_request_id, build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: BuildRequestStatus::BuildRequestReceived as i32, - status_name: BuildRequestStatus::BuildRequestReceived.to_display_string(), + status: Some(BuildRequestStatusCode::BuildRequestReceived.status()), requested_partitions, message: "Build request received".to_string(), + comment: None, + want_id: None, }), ); @@ -57,10 +58,11 @@ impl EventWriter { let event = create_build_event( build_request_id, build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: status as i32, - status_name: status.to_display_string(), + status: Some(status), requested_partitions: vec![], message, + comment: None, + want_id: None, }), ); @@ -80,10 +82,11 @@ impl EventWriter { let event = create_build_event( build_request_id, build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: status as i32, - status_name: status.to_display_string(), + status: Some(status), requested_partitions, message, + comment: None, + want_id: None, }), ); @@ -104,7 +107,7 @@ impl EventWriter { let event = BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id, + build_request_id: Some(build_request_id), event_type: Some(build_event::EventType::PartitionEvent(PartitionEvent { partition_ref: Some(partition_ref), status_code: status as i32, @@ -136,7 +139,7 @@ impl EventWriter { let event = BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id, + build_request_id: Some(build_request_id), event_type: Some(build_event::EventType::PartitionInvalidationEvent( PartitionInvalidationEvent { partition_ref: Some(partition_ref), @@ -162,7 +165,7 @@ impl EventWriter { let event = BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id, + build_request_id: Some(build_request_id), event_type: Some(build_event::EventType::JobEvent(JobEvent { job_run_id, job_label: Some(job_label), @@ -194,7 +197,7 @@ impl EventWriter { let event = BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id, + build_request_id: Some(build_request_id), event_type: Some(build_event::EventType::JobEvent(JobEvent { job_run_id, job_label: Some(job_label), @@ -256,7 +259,7 @@ impl EventWriter { let event = BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id, + build_request_id: Some(build_request_id), event_type: Some(build_event::EventType::JobRunCancelEvent(JobRunCancelEvent { job_run_id, reason, @@ -285,22 +288,22 @@ impl EventWriter { let latest_status = build_events.iter() .rev() .find_map(|e| match &e.event_type { - Some(build_event::EventType::BuildRequestEvent(br)) => Some(br.status_code), + Some(build_event::EventType::BuildRequestEvent(br)) => Some(br.clone().status.unwrap().code), _ => None, }); match latest_status { - Some(status) if status == BuildRequestStatus::BuildRequestCompleted as i32 => { + Some(status) if status == BuildRequestStatusCode::BuildRequestCompleted as i32 => { return Err(BuildEventLogError::QueryError( format!("Cannot cancel completed build: {}", build_request_id) )); } - Some(status) if status == BuildRequestStatus::BuildRequestFailed as i32 => { + Some(status) if status == BuildRequestStatusCode::BuildRequestFailed as i32 => { return Err(BuildEventLogError::QueryError( format!("Cannot cancel failed build: {}", build_request_id) )); } - Some(status) if status == BuildRequestStatus::BuildRequestCancelled as i32 => { + Some(status) if status == BuildRequestStatusCode::BuildRequestCancelled as i32 => { return Err(BuildEventLogError::QueryError( format!("Build already cancelled: {}", build_request_id) )); @@ -311,7 +314,7 @@ impl EventWriter { let event = BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id: build_request_id.clone(), + build_request_id: Some(build_request_id.clone()), event_type: Some(build_event::EventType::BuildCancelEvent(BuildCancelEvent { reason, })), @@ -322,7 +325,7 @@ impl EventWriter { // Also emit a build request status update self.update_build_status( build_request_id, - BuildRequestStatus::BuildRequestCancelled, + BuildRequestStatusCode::BuildRequestCancelled.status(), "Build cancelled by user".to_string(), ).await } @@ -361,7 +364,7 @@ impl EventWriter { let event = BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id, + build_request_id: Some(build_request_id), event_type: Some(build_event::EventType::JobGraphEvent(JobGraphEvent { job_graph: Some(job_graph), message, @@ -391,19 +394,19 @@ mod tests { // Test status updates writer.update_build_status( build_id.clone(), - BuildRequestStatus::BuildRequestPlanning, + BuildRequestStatusCode::BuildRequestPlanning.status(), "Starting planning".to_string(), ).await.unwrap(); writer.update_build_status( build_id.clone(), - BuildRequestStatus::BuildRequestExecuting, + BuildRequestStatusCode::BuildRequestExecuting.status(), "Starting execution".to_string(), ).await.unwrap(); writer.update_build_status( build_id.clone(), - BuildRequestStatus::BuildRequestCompleted, + BuildRequestStatusCode::BuildRequestCompleted.status(), "Build completed successfully".to_string(), ).await.unwrap(); } diff --git a/databuild/format_consistency_test.rs b/databuild/format_consistency_test.rs index 9ed3931..bf000ef 100644 --- a/databuild/format_consistency_test.rs +++ b/databuild/format_consistency_test.rs @@ -107,9 +107,8 @@ mod format_consistency_tests { assert_eq!(JobStatus::from_display_string("completed"), Some(job_status)); // Test BuildRequestStatus conversions - let build_status = BuildRequestStatus::BuildRequestCompleted; - assert_eq!(build_status.to_display_string(), "completed"); - assert_eq!(BuildRequestStatus::from_display_string("completed"), Some(build_status)); + let build_status = BuildRequestStatusCode::BuildRequestCompleted.status(); + assert_eq!(build_status.name, "completed"); // Test invalid conversions assert_eq!(PartitionStatus::from_display_string("invalid"), None); diff --git a/databuild/graph/analyze.rs b/databuild/graph/analyze.rs index 30b60b4..31f1f65 100644 --- a/databuild/graph/analyze.rs +++ b/databuild/graph/analyze.rs @@ -203,10 +203,11 @@ async fn plan( let event = create_build_event( build_request_id.to_string(), crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: BuildRequestStatus::BuildRequestReceived as i32, - status_name: BuildRequestStatus::BuildRequestReceived.to_display_string(), + status: Some(BuildRequestStatusCode::BuildRequestReceived.status()), requested_partitions: output_refs.iter().map(|s| PartitionRef { str: s.clone() }).collect(), message: "Analysis started".to_string(), + comment: None, + want_id: None, }) ); if let Err(e) = query_engine_ref.append_event(event).await { @@ -264,10 +265,11 @@ async fn plan( let event = create_build_event( build_request_id.to_string(), crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: BuildRequestStatus::BuildRequestPlanning as i32, - status_name: BuildRequestStatus::BuildRequestPlanning.to_display_string(), + status: Some(BuildRequestStatusCode::BuildRequestPlanning.status()), requested_partitions: output_refs.iter().map(|s| PartitionRef { str: s.clone() }).collect(), message: "Graph analysis in progress".to_string(), + comment: None, + want_id: None, }) ); if let Err(e) = query_engine_ref.append_event(event).await { @@ -334,10 +336,11 @@ async fn plan( let event = create_build_event( build_request_id.to_string(), crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: BuildRequestStatus::BuildRequestAnalysisCompleted as i32, - status_name: BuildRequestStatus::BuildRequestAnalysisCompleted.to_display_string(), + status: Some(BuildRequestStatusCode::BuildRequestAnalysisCompleted.status()), requested_partitions: output_refs.iter().map(|s| PartitionRef { str: s.clone() }).collect(), message: format!("Analysis completed successfully, {} tasks planned", nodes.len()), + comment: None, + want_id: None, }) ); if let Err(e) = query_engine.append_event(event).await { @@ -376,10 +379,11 @@ async fn plan( let event = create_build_event( build_request_id.to_string(), crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: BuildRequestStatus::BuildRequestFailed as i32, - status_name: BuildRequestStatus::BuildRequestFailed.to_display_string(), + status: Some(BuildRequestStatusCode::BuildRequestFailed.status()), requested_partitions: output_refs.iter().map(|s| PartitionRef { str: s.clone() }).collect(), message: "No jobs found for requested partitions".to_string(), + comment: None, + want_id: None, }) ); if let Err(e) = query_engine.append_event(event).await { diff --git a/databuild/graph/execute.rs b/databuild/graph/execute.rs index 35f1053..7d962d7 100644 --- a/databuild/graph/execute.rs +++ b/databuild/graph/execute.rs @@ -1,4 +1,4 @@ -use databuild::{JobGraph, Task, JobStatus, BuildRequestStatus, PartitionStatus, BuildRequestEvent, JobEvent, PartitionEvent, PartitionRef}; +use databuild::{JobGraph, Task, JobStatus, BuildRequestStatus, BuildRequestStatusCode, PartitionStatus, BuildRequestEvent, JobEvent, PartitionEvent, PartitionRef}; use databuild::event_log::{create_bel_query_engine, create_build_event}; use databuild::build_event::EventType; use databuild::log_collector::{LogCollector, LogCollectorError}; @@ -460,10 +460,11 @@ async fn main() -> Result<(), Box> { let event = create_build_event( build_request_id.clone(), EventType::BuildRequestEvent(BuildRequestEvent { - status_code: BuildRequestStatus::BuildRequestExecuting as i32, - status_name: BuildRequestStatus::BuildRequestExecuting.to_display_string(), + status: Some(BuildRequestStatusCode::BuildRequestExecuting.status()), requested_partitions: graph.outputs.clone(), message: format!("Starting execution of {} jobs", graph.nodes.len()), + comment: None, + want_id: None, }) ); if let Err(e) = query_engine.append_event(event).await { @@ -787,18 +788,19 @@ async fn main() -> Result<(), Box> { // Log final build request status (existing detailed event) if let Some(ref query_engine) = build_event_log { let final_status = if failure_count > 0 || fail_fast_triggered { - BuildRequestStatus::BuildRequestFailed + BuildRequestStatusCode::BuildRequestFailed } else { - BuildRequestStatus::BuildRequestCompleted + BuildRequestStatusCode::BuildRequestCompleted }; let event = create_build_event( build_request_id.clone(), EventType::BuildRequestEvent(BuildRequestEvent { - status_code: final_status as i32, - status_name: final_status.to_display_string(), + status: Some(final_status.status()), requested_partitions: graph.outputs.clone(), message: format!("Execution completed: {} succeeded, {} failed", success_count, failure_count), + comment: None, + want_id: None, }) ); if let Err(e) = query_engine.append_event(event).await { diff --git a/databuild/orchestration/events.rs b/databuild/orchestration/events.rs index efbee01..5854ef5 100644 --- a/databuild/orchestration/events.rs +++ b/databuild/orchestration/events.rs @@ -10,10 +10,11 @@ pub fn create_build_request_received_event( create_build_event( build_request_id, build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: BuildRequestStatus::BuildRequestReceived as i32, - status_name: BuildRequestStatus::BuildRequestReceived.to_display_string(), + status: Some(BuildRequestStatusCode::BuildRequestReceived.status()), requested_partitions, message: "Build request received".to_string(), + comment: None, + want_id: None, }), ) } @@ -24,10 +25,11 @@ pub fn create_build_planning_started_event( create_build_event( build_request_id, build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: BuildRequestStatus::BuildRequestPlanning as i32, - status_name: BuildRequestStatus::BuildRequestPlanning.to_display_string(), + status: Some(BuildRequestStatusCode::BuildRequestPlanning.status()), requested_partitions: vec![], message: "Starting build planning".to_string(), + comment: None, + want_id: None, }), ) } @@ -38,10 +40,11 @@ pub fn create_build_execution_started_event( create_build_event( build_request_id, build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: BuildRequestStatus::BuildRequestExecuting as i32, - status_name: BuildRequestStatus::BuildRequestExecuting.to_display_string(), + status: Some(BuildRequestStatusCode::BuildRequestExecuting.status()), requested_partitions: vec![], message: "Starting build execution".to_string(), + comment: None, + want_id: None, }), ) } @@ -63,17 +66,18 @@ pub fn create_build_completed_event( }; let status = match result { - super::BuildResult::Success { .. } => BuildRequestStatus::BuildRequestCompleted, - super::BuildResult::Failed { .. } | super::BuildResult::FailFast { .. } => BuildRequestStatus::BuildRequestFailed, + super::BuildResult::Success { .. } => BuildRequestStatusCode::BuildRequestCompleted.status(), + super::BuildResult::Failed { .. } | super::BuildResult::FailFast { .. } => BuildRequestStatusCode::BuildRequestFailed.status(), }; create_build_event( build_request_id, build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: status as i32, - status_name: status.to_display_string(), + status: Some(status), requested_partitions: vec![], message, + comment: None, + want_id: None, }), ) } @@ -86,10 +90,11 @@ pub fn create_analysis_completed_event( create_build_event( build_request_id, build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: BuildRequestStatus::BuildRequestAnalysisCompleted as i32, - status_name: BuildRequestStatus::BuildRequestAnalysisCompleted.to_display_string(), + status: Some(BuildRequestStatusCode::BuildRequestAnalysisCompleted.status()), requested_partitions, message: format!("Analysis completed successfully, {} tasks planned", task_count), + comment: None, + want_id: None, }), ) } @@ -101,7 +106,7 @@ pub fn create_job_scheduled_event( BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id, + build_request_id: Some(build_request_id), event_type: Some(build_event::EventType::JobEvent(job_event.clone())), } } @@ -113,7 +118,7 @@ pub fn create_job_completed_event( BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id, + build_request_id: Some(build_request_id), event_type: Some(build_event::EventType::JobEvent(job_event.clone())), } } @@ -125,7 +130,7 @@ pub fn create_partition_available_event( BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), - build_request_id, + build_request_id: Some(build_request_id), event_type: Some(build_event::EventType::PartitionEvent(partition_event.clone())), } } diff --git a/databuild/orchestration/mod.rs b/databuild/orchestration/mod.rs index f2564d5..e048583 100644 --- a/databuild/orchestration/mod.rs +++ b/databuild/orchestration/mod.rs @@ -66,7 +66,7 @@ impl BuildOrchestrator { self.event_writer.update_build_status( self.build_request_id.clone(), - BuildRequestStatus::BuildRequestPlanning, + BuildRequestStatusCode::BuildRequestPlanning.status(), "Starting build planning".to_string(), ).await .map_err(OrchestrationError::EventLog)?; @@ -80,7 +80,7 @@ impl BuildOrchestrator { self.event_writer.update_build_status( self.build_request_id.clone(), - BuildRequestStatus::BuildRequestExecuting, + BuildRequestStatusCode::BuildRequestExecuting.status(), "Starting build execution".to_string(), ).await .map_err(OrchestrationError::EventLog)?; @@ -95,22 +95,22 @@ impl BuildOrchestrator { let (status, message) = match &result { BuildResult::Success { jobs_completed } => { - (BuildRequestStatus::BuildRequestCompleted, + (BuildRequestStatusCode::BuildRequestCompleted, format!("Build completed successfully with {} jobs", jobs_completed)) } BuildResult::Failed { jobs_completed, jobs_failed } => { - (BuildRequestStatus::BuildRequestFailed, + (BuildRequestStatusCode::BuildRequestFailed, format!("Build failed: {} jobs completed, {} jobs failed", jobs_completed, jobs_failed)) } BuildResult::FailFast { trigger_job } => { - (BuildRequestStatus::BuildRequestFailed, + (BuildRequestStatusCode::BuildRequestFailed, format!("Build failed fast due to job: {}", trigger_job)) } }; self.event_writer.update_build_status( self.build_request_id.clone(), - status, + status.status(), message, ).await .map_err(OrchestrationError::EventLog)?; @@ -122,7 +122,7 @@ impl BuildOrchestrator { pub async fn emit_analysis_completed(&self, task_count: usize) -> Result<()> { self.event_writer.update_build_status_with_partitions( self.build_request_id.clone(), - BuildRequestStatus::BuildRequestAnalysisCompleted, + BuildRequestStatusCode::BuildRequestAnalysisCompleted.status(), self.requested_partitions.clone(), format!("Analysis completed successfully, {} tasks planned", task_count), ).await diff --git a/databuild/repositories/builds/mod.rs b/databuild/repositories/builds/mod.rs index 6c3de23..942da9a 100644 --- a/databuild/repositories/builds/mod.rs +++ b/databuild/repositories/builds/mod.rs @@ -62,7 +62,7 @@ impl BuildsRepository { let builds = response.builds.into_iter().map(|build| { BuildInfo { build_request_id: build.build_request_id, - status: BuildRequestStatus::try_from(build.status_code).unwrap_or(BuildRequestStatus::BuildRequestUnknown), + status: build.status.clone().unwrap_or(BuildRequestStatusCode::BuildRequestUnknown.status()), requested_partitions: build.requested_partitions, requested_at: build.requested_at, started_at: build.started_at, @@ -116,7 +116,7 @@ impl BuildsRepository { let mut timeline = Vec::new(); for event in all_events { if let Some(crate::build_event::EventType::BuildRequestEvent(br_event)) = &event.event_type { - if let Ok(status) = BuildRequestStatus::try_from(br_event.status_code) { + if let Some(status) = br_event.clone().status { timeline.push(BuildEvent { timestamp: event.timestamp, event_type: "build_status".to_string(), @@ -151,8 +151,7 @@ impl BuildsRepository { .into_iter() .map(|event| ServiceBuildTimelineEvent { timestamp: event.timestamp, - status_code: event.status.map(|s| s as i32), - status_name: event.status.map(|s| s.to_display_string()), + status: event.status, message: event.message, event_type: event.event_type, cancel_reason: event.cancel_reason, @@ -161,8 +160,7 @@ impl BuildsRepository { let response = BuildDetailResponse { build_request_id: build_info.build_request_id, - status_code: build_info.status as i32, - status_name: build_info.status.to_display_string(), + status: Some(build_info.status), requested_partitions: build_info.requested_partitions, total_jobs: build_info.total_jobs as u32, completed_jobs: build_info.completed_jobs as u32, @@ -200,18 +198,18 @@ impl BuildsRepository { let (build, _timeline) = build_info.unwrap(); // Check if build is in a cancellable state - match build.status { - BuildRequestStatus::BuildRequestCompleted => { + match BuildRequestStatusCode::try_from(build.status.code) { + Ok(BuildRequestStatusCode::BuildRequestCompleted) => { return Err(BuildEventLogError::QueryError( format!("Cannot cancel completed build: {}", build_request_id) )); } - BuildRequestStatus::BuildRequestFailed => { + Ok(BuildRequestStatusCode::BuildRequestFailed) => { return Err(BuildEventLogError::QueryError( format!("Cannot cancel failed build: {}", build_request_id) )); } - BuildRequestStatus::BuildRequestCancelled => { + Ok(BuildRequestStatusCode::BuildRequestCancelled) => { return Err(BuildEventLogError::QueryError( format!("Build already cancelled: {}", build_request_id) )); @@ -225,10 +223,11 @@ impl BuildsRepository { let cancel_event = create_build_event( build_request_id.to_string(), crate::build_event::EventType::BuildRequestEvent(crate::BuildRequestEvent { - status_code: BuildRequestStatus::BuildRequestCancelled as i32, - status_name: BuildRequestStatus::BuildRequestCancelled.to_display_string(), + status: Some(BuildRequestStatusCode::BuildRequestCancelled.status()), requested_partitions: build.requested_partitions, message: format!("Build cancelled"), + comment: None, + want_id: None, }) ); @@ -250,8 +249,7 @@ impl BuildsRepository { .into_iter() .map(|build| crate::BuildSummary { build_request_id: build.build_request_id, - status_code: build.status as i32, - status_name: build.status.to_display_string(), + status: Some(build.status), requested_partitions: build.requested_partitions.into_iter().map(|p| crate::PartitionRef { str: p.str }).collect(), total_jobs: build.total_jobs as u32, completed_jobs: build.completed_jobs as u32, @@ -262,6 +260,7 @@ impl BuildsRepository { completed_at: build.completed_at, duration_ms: build.duration_ms, cancelled: build.cancelled, + comment: None, }) .collect(); @@ -292,10 +291,10 @@ mod tests { // Create events for multiple builds let events = vec![ - test_events::build_request_event(Some(build_id1.clone()), vec![partition1.clone()], BuildRequestStatus::BuildRequestReceived), - test_events::build_request_event(Some(build_id1.clone()), vec![partition1.clone()], BuildRequestStatus::BuildRequestCompleted), - test_events::build_request_event(Some(build_id2.clone()), vec![partition2.clone()], BuildRequestStatus::BuildRequestReceived), - test_events::build_request_event(Some(build_id2.clone()), vec![partition2.clone()], BuildRequestStatus::BuildRequestFailed), + test_events::build_request_event(Some(build_id1.clone()), vec![partition1.clone()], BuildRequestStatusCode::BuildRequestReceived.status()), + test_events::build_request_event(Some(build_id1.clone()), vec![partition1.clone()], BuildRequestStatusCode::BuildRequestCompleted.status()), + test_events::build_request_event(Some(build_id2.clone()), vec![partition2.clone()], BuildRequestStatusCode::BuildRequestReceived.status()), + test_events::build_request_event(Some(build_id2.clone()), vec![partition2.clone()], BuildRequestStatusCode::BuildRequestFailed.status()), ]; let query_engine = create_mock_bel_query_engine_with_events(events).await.unwrap(); @@ -308,11 +307,11 @@ mod tests { let build1 = builds.iter().find(|b| b.build_request_id == build_id1).unwrap(); let build2 = builds.iter().find(|b| b.build_request_id == build_id2).unwrap(); - assert_eq!(build1.status, BuildRequestStatus::BuildRequestCompleted); + assert_eq!(build1.status, BuildRequestStatusCode::BuildRequestCompleted.status()); assert_eq!(build1.requested_partitions.len(), 1); assert!(!build1.cancelled); - assert_eq!(build2.status, BuildRequestStatus::BuildRequestFailed); + assert_eq!(build2.status, BuildRequestStatusCode::BuildRequestFailed.status()); assert_eq!(build2.requested_partitions.len(), 1); assert!(!build2.cancelled); } @@ -323,10 +322,10 @@ mod tests { let partition = PartitionRef { str: "analytics/daily".to_string() }; let events = vec![ - test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatus::BuildRequestReceived), - test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatus::BuildRequestPlanning), - test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatus::BuildRequestExecuting), - test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatus::BuildRequestCompleted), + test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatusCode::BuildRequestReceived.status()), + test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatusCode::BuildRequestPlanning.status()), + test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatusCode::BuildRequestExecuting.status()), + test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatusCode::BuildRequestCompleted.status()), ]; let query_engine = create_mock_bel_query_engine_with_events(events).await.unwrap(); @@ -337,14 +336,14 @@ mod tests { let (info, timeline) = result.unwrap(); assert_eq!(info.build_request_id, build_id); - assert_eq!(info.status, BuildRequestStatus::BuildRequestCompleted); + assert_eq!(info.status, BuildRequestStatusCode::BuildRequestCompleted.status()); assert!(!info.cancelled); assert_eq!(timeline.len(), 4); - assert_eq!(timeline[0].status, Some(BuildRequestStatus::BuildRequestReceived)); - assert_eq!(timeline[1].status, Some(BuildRequestStatus::BuildRequestPlanning)); - assert_eq!(timeline[2].status, Some(BuildRequestStatus::BuildRequestExecuting)); - assert_eq!(timeline[3].status, Some(BuildRequestStatus::BuildRequestCompleted)); + assert_eq!(timeline[0].status, Some(BuildRequestStatusCode::BuildRequestReceived.status())); + assert_eq!(timeline[1].status, Some(BuildRequestStatusCode::BuildRequestPlanning.status())); + assert_eq!(timeline[2].status, Some(BuildRequestStatusCode::BuildRequestExecuting.status())); + assert_eq!(timeline[3].status, Some(BuildRequestStatusCode::BuildRequestCompleted.status())); } #[tokio::test] @@ -363,8 +362,8 @@ mod tests { // Start with a running build let events = vec![ - test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatus::BuildRequestReceived), - test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatus::BuildRequestExecuting), + test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatusCode::BuildRequestReceived.status()), + test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatusCode::BuildRequestExecuting.status()), ]; let query_engine = create_mock_bel_query_engine_with_events(events).await.unwrap(); @@ -389,8 +388,8 @@ mod tests { // Create a completed build let events = vec![ - test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatus::BuildRequestReceived), - test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatus::BuildRequestCompleted), + test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatusCode::BuildRequestReceived.status()), + test_events::build_request_event(Some(build_id.clone()), vec![partition.clone()], BuildRequestStatusCode::BuildRequestCompleted.status()), ]; let query_engine = create_mock_bel_query_engine_with_events(events).await.unwrap(); diff --git a/databuild/repositories/jobs/mod.rs b/databuild/repositories/jobs/mod.rs index 6c21741..3ef6049 100644 --- a/databuild/repositories/jobs/mod.rs +++ b/databuild/repositories/jobs/mod.rs @@ -102,7 +102,7 @@ impl JobsRepository { let job_run = JobRunDetail { job_run_id: j_event.job_run_id.clone(), job_label: job_label.clone(), - build_request_id: event.build_request_id.clone(), + build_request_id: event.build_request_id.clone().unwrap(), target_partitions: j_event.target_partitions.clone(), status, scheduled_at: event.timestamp, @@ -229,7 +229,7 @@ impl JobsRepository { let job_run = JobRunDetail { job_run_id: j_event.job_run_id.clone(), job_label: job_label.to_string(), - build_request_id: event.build_request_id.clone(), + build_request_id: event.build_request_id.clone().unwrap(), target_partitions: j_event.target_partitions.clone(), status, scheduled_at: event.timestamp, diff --git a/databuild/repositories/partitions/mod.rs b/databuild/repositories/partitions/mod.rs index b1b2317..6034b36 100644 --- a/databuild/repositories/partitions/mod.rs +++ b/databuild/repositories/partitions/mod.rs @@ -164,7 +164,7 @@ impl PartitionsRepository { timestamp: event.timestamp, status: event_status, message: p_event.message.clone(), - build_request_id: event.build_request_id, + build_request_id: event.build_request_id.unwrap(), job_run_id: if p_event.job_run_id.is_empty() { None } else { Some(p_event.job_run_id.clone()) }, }); } diff --git a/databuild/repositories/tasks/mod.rs b/databuild/repositories/tasks/mod.rs index 0910252..ba77c5f 100644 --- a/databuild/repositories/tasks/mod.rs +++ b/databuild/repositories/tasks/mod.rs @@ -86,7 +86,7 @@ impl TasksRepository { TaskInfo { job_run_id: j_event.job_run_id.clone(), job_label: job_label.clone(), - build_request_id: event.build_request_id.clone(), + build_request_id: event.build_request_id.clone().unwrap(), status: JobStatus::JobUnknown, target_partitions: j_event.target_partitions.clone(), scheduled_at: event.timestamp, @@ -182,7 +182,7 @@ impl TasksRepository { task_info = Some(TaskInfo { job_run_id: j_event.job_run_id.clone(), job_label: job_label.clone(), - build_request_id: event.build_request_id.clone(), + build_request_id: event.build_request_id.clone().unwrap(), status: JobStatus::JobUnknown, target_partitions: j_event.target_partitions.clone(), scheduled_at: event.timestamp, diff --git a/databuild/service/handlers.rs b/databuild/service/handlers.rs index d0a8c48..2a49198 100644 --- a/databuild/service/handlers.rs +++ b/databuild/service/handlers.rs @@ -61,7 +61,7 @@ pub async fn submit_build_request( // Create build request state let build_state = BuildRequestState { build_request_id: build_request_id.clone(), - status: BuildRequestStatus::BuildRequestReceived, + status: BuildRequestStatusCode::BuildRequestReceived.status(), requested_partitions: request.partitions.clone(), created_at: timestamp, updated_at: timestamp, @@ -160,7 +160,7 @@ pub async fn cancel_build_request( { let mut active_builds = service.active_builds.write().await; if let Some(build_state) = active_builds.get_mut(&build_request_id) { - build_state.status = BuildRequestStatus::BuildRequestCancelled; + build_state.status = BuildRequestStatusCode::BuildRequestCancelled.status(); build_state.updated_at = current_timestamp_nanos(); } else { return Err(( @@ -176,10 +176,11 @@ pub async fn cancel_build_request( let event = create_build_event( build_request_id.clone(), crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent { - status_code: BuildRequestStatus::BuildRequestCancelled as i32, - status_name: BuildRequestStatus::BuildRequestCancelled.to_display_string(), + status: Some(BuildRequestStatusCode::BuildRequestCancelled.status()), requested_partitions: vec![], message: "Build request cancelled".to_string(), + comment: None, + want_id: None, }), ); @@ -262,14 +263,14 @@ pub async fn get_partition_events( let decoded_partition_ref = base64_url_decode(&partition_ref).unwrap(); let events = match service.query_engine.get_partition_events(&decoded_partition_ref, None).await { - Ok(events) => events.into_iter().map(|e| { + Ok(events) => events.into_iter().filter(|e| e.build_request_id.is_some()).map(|e| { let (job_label, partition_ref, delegated_build_id) = extract_navigation_data(&e.event_type); BuildEventSummary { event_id: e.event_id, timestamp: e.timestamp, event_type: event_type_to_string(&e.event_type), message: event_to_message(&e.event_type), - build_request_id: e.build_request_id, + build_request_id: e.build_request_id.clone().unwrap(), job_label, partition_ref, delegated_build_id, @@ -350,7 +351,7 @@ async fn execute_build_request( ); // Update status to planning - update_build_request_status(&service, &build_request_id, BuildRequestStatus::BuildRequestPlanning).await; + update_build_request_status(&service, &build_request_id, BuildRequestStatusCode::BuildRequestPlanning.status()).await; // Log planning event if let Err(e) = orchestrator.start_planning().await { @@ -362,7 +363,7 @@ async fn execute_build_request( Ok(graph) => graph, Err(e) => { error!("Failed to analyze build graph: {}", e); - update_build_request_status(&service, &build_request_id, BuildRequestStatus::BuildRequestFailed).await; + update_build_request_status(&service, &build_request_id, BuildRequestStatusCode::BuildRequestFailed.status()).await; // Log failure event if let Err(log_err) = orchestrator.complete_build(BuildResult::Failed { jobs_completed: 0, jobs_failed: 1 }).await { @@ -375,7 +376,7 @@ async fn execute_build_request( // Update status to executing - update_build_request_status(&service, &build_request_id, BuildRequestStatus::BuildRequestExecuting).await; + update_build_request_status(&service, &build_request_id, BuildRequestStatusCode::BuildRequestExecuting.status()).await; // Log executing event if let Err(e) = orchestrator.start_execution().await { @@ -386,7 +387,7 @@ async fn execute_build_request( match run_execute_command(&service, &build_request_id, &job_graph).await { Ok(_) => { info!("Build request {} completed successfully", build_request_id); - update_build_request_status(&service, &build_request_id, BuildRequestStatus::BuildRequestCompleted).await; + update_build_request_status(&service, &build_request_id, BuildRequestStatusCode::BuildRequestCompleted.status()).await; // Log completion event if let Err(e) = orchestrator.complete_build(BuildResult::Success { jobs_completed: 0 }).await { @@ -397,7 +398,7 @@ async fn execute_build_request( } Err(e) => { error!("Build request {} failed: {}", build_request_id, e); - update_build_request_status(&service, &build_request_id, BuildRequestStatus::BuildRequestFailed).await; + update_build_request_status(&service, &build_request_id, BuildRequestStatusCode::BuildRequestFailed.status()).await; // Log failure event if let Err(log_err) = orchestrator.complete_build(BuildResult::Failed { jobs_completed: 0, jobs_failed: 1 }).await { @@ -505,7 +506,9 @@ fn event_type_to_string(event_type: &Option) -> S Some(crate::build_event::EventType::PartitionInvalidationEvent(_)) => "partition_invalidation".to_string(), Some(crate::build_event::EventType::JobRunCancelEvent(_)) => "task_cancel".to_string(), Some(crate::build_event::EventType::BuildCancelEvent(_)) => "build_cancel".to_string(), - None => "INVALID_EVENT_TYPE".to_string(), // Make this obvious rather than hiding it + Some(build_event::EventType::WantEvent(_)) => "want".to_string(), + Some(build_event::EventType::TaintEvent(_)) => "taint".to_string(), + None => "INVALID_EVENT_TYPE".to_string(), } } @@ -519,7 +522,10 @@ fn event_to_message(event_type: &Option) -> Strin Some(crate::build_event::EventType::PartitionInvalidationEvent(event)) => event.reason.clone(), Some(crate::build_event::EventType::JobRunCancelEvent(event)) => event.reason.clone(), Some(crate::build_event::EventType::BuildCancelEvent(event)) => event.reason.clone(), - None => "INVALID_EVENT_NO_MESSAGE".to_string(), // Make this obvious + Some(build_event::EventType::WantEvent(event)) => event.comment.clone(), + Some(build_event::EventType::TaintEvent(event)) => event.comment.clone(), + + None => "INVALID_EVENT_NO_MESSAGE".to_string(), } } @@ -557,6 +563,12 @@ fn extract_navigation_data(event_type: &Option) - // Build cancel events don't need navigation links (None, None, None) }, + Some(crate::build_event::EventType::WantEvent(_)) => { + (None, None, None) + }, + Some(crate::build_event::EventType::TaintEvent(_)) => { + (None, None, None) + }, None => (None, None, None), } } @@ -1417,8 +1429,7 @@ pub async fn get_build_detail( let timeline_events: Vec = protobuf_response.timeline.into_iter().map(|event| { BuildTimelineEvent { timestamp: event.timestamp, - status_code: event.status_code, - status_name: event.status_name, + status: event.status, message: event.message, event_type: event.event_type, cancel_reason: event.cancel_reason, @@ -1427,8 +1438,7 @@ pub async fn get_build_detail( Ok(Json(BuildDetailResponse { build_request_id: protobuf_response.build_request_id, - status_code: protobuf_response.status_code, - status_name: protobuf_response.status_name, + status: protobuf_response.status, requested_partitions: protobuf_response.requested_partitions, total_jobs: protobuf_response.total_jobs, completed_jobs: protobuf_response.completed_jobs, diff --git a/databuild/service/mod.rs b/databuild/service/mod.rs index 6e86244..98b6514 100644 --- a/databuild/service/mod.rs +++ b/databuild/service/mod.rs @@ -13,6 +13,7 @@ use serde::{Deserialize, Serialize}; use schemars::JsonSchema; use std::collections::HashMap; use std::sync::Arc; +use rusqlite::ToSql; use tokio::sync::RwLock; use uuid::Uuid; @@ -398,15 +399,17 @@ impl BuildGraphService { } pub fn status_to_string(status: BuildRequestStatus) -> String { - match status { - BuildRequestStatus::BuildRequestUnknown => "unknown".to_string(), - BuildRequestStatus::BuildRequestReceived => "received".to_string(), - BuildRequestStatus::BuildRequestPlanning => "planning".to_string(), - BuildRequestStatus::BuildRequestAnalysisCompleted => "analysis_completed".to_string(), - BuildRequestStatus::BuildRequestExecuting => "executing".to_string(), - BuildRequestStatus::BuildRequestCompleted => "completed".to_string(), - BuildRequestStatus::BuildRequestFailed => "failed".to_string(), - BuildRequestStatus::BuildRequestCancelled => "cancelled".to_string(), + match BuildRequestStatusCode::try_from(status.code) { + Ok(BuildRequestStatusCode::BuildRequestUnknown) => "unknown".to_string(), + Ok(BuildRequestStatusCode::BuildRequestReceived) => "received".to_string(), + Ok(BuildRequestStatusCode::BuildRequestPlanning) => "planning".to_string(), + Ok(BuildRequestStatusCode::BuildRequestAnalysisCompleted) => "analysis_completed".to_string(), + Ok(BuildRequestStatusCode::BuildRequestExecuting) => "executing".to_string(), + Ok(BuildRequestStatusCode::BuildRequestCompleted) => "completed".to_string(), + Ok(BuildRequestStatusCode::BuildRequestFailed) => "failed".to_string(), + Ok(BuildRequestStatusCode::BuildRequestCancelled) => "cancelled".to_string(), + Ok(BuildRequestStatusCode::BuildRequestPreconditionFailed) => "precondition_failed".to_string(), + Err(_) => "error".to_string(), } } diff --git a/databuild/status_utils.rs b/databuild/status_utils.rs index f553e0e..6d66b9b 100644 --- a/databuild/status_utils.rs +++ b/databuild/status_utils.rs @@ -61,35 +61,44 @@ impl JobStatus { } } -impl BuildRequestStatus { +impl BuildRequestStatusCode { /// Convert build request status to human-readable string matching current CLI/service format pub fn to_display_string(&self) -> String { match self { - BuildRequestStatus::BuildRequestUnknown => "unknown".to_string(), - BuildRequestStatus::BuildRequestReceived => "received".to_string(), - BuildRequestStatus::BuildRequestPlanning => "planning".to_string(), - BuildRequestStatus::BuildRequestAnalysisCompleted => "analysis_completed".to_string(), - BuildRequestStatus::BuildRequestExecuting => "executing".to_string(), - BuildRequestStatus::BuildRequestCompleted => "completed".to_string(), - BuildRequestStatus::BuildRequestFailed => "failed".to_string(), - BuildRequestStatus::BuildRequestCancelled => "cancelled".to_string(), + BuildRequestStatusCode::BuildRequestUnknown => "unknown".to_string(), + BuildRequestStatusCode::BuildRequestReceived => "received".to_string(), + BuildRequestStatusCode::BuildRequestPlanning => "planning".to_string(), + BuildRequestStatusCode::BuildRequestAnalysisCompleted => "analysis_completed".to_string(), + BuildRequestStatusCode::BuildRequestExecuting => "executing".to_string(), + BuildRequestStatusCode::BuildRequestCompleted => "completed".to_string(), + BuildRequestStatusCode::BuildRequestFailed => "failed".to_string(), + BuildRequestStatusCode::BuildRequestCancelled => "cancelled".to_string(), + &BuildRequestStatusCode::BuildRequestPreconditionFailed => "precondition failed".to_string(), } } /// Parse a display string back to enum pub fn from_display_string(s: &str) -> Option { match s { - "unknown" => Some(BuildRequestStatus::BuildRequestUnknown), - "received" => Some(BuildRequestStatus::BuildRequestReceived), - "planning" => Some(BuildRequestStatus::BuildRequestPlanning), - "analysis_completed" => Some(BuildRequestStatus::BuildRequestAnalysisCompleted), - "executing" => Some(BuildRequestStatus::BuildRequestExecuting), - "completed" => Some(BuildRequestStatus::BuildRequestCompleted), - "failed" => Some(BuildRequestStatus::BuildRequestFailed), - "cancelled" => Some(BuildRequestStatus::BuildRequestCancelled), + "unknown" => Some(BuildRequestStatusCode::BuildRequestUnknown), + "received" => Some(BuildRequestStatusCode::BuildRequestReceived), + "planning" => Some(BuildRequestStatusCode::BuildRequestPlanning), + "analysis_completed" => Some(BuildRequestStatusCode::BuildRequestAnalysisCompleted), + "executing" => Some(BuildRequestStatusCode::BuildRequestExecuting), + "completed" => Some(BuildRequestStatusCode::BuildRequestCompleted), + "failed" => Some(BuildRequestStatusCode::BuildRequestFailed), + "cancelled" => Some(BuildRequestStatusCode::BuildRequestCancelled), + "precondition failed" => Some(BuildRequestStatusCode::BuildRequestPreconditionFailed), _ => None, } } + + pub fn status(&self) -> BuildRequestStatus { + BuildRequestStatus { + code: self.clone().into(), + name: self.to_display_string(), + } + } } impl DepType { @@ -205,11 +214,11 @@ pub mod list_response_helpers { completed_at: Option, duration_ms: Option, cancelled: bool, + comment: Option, ) -> BuildSummary { BuildSummary { build_request_id, - status_code: status as i32, - status_name: status.to_display_string(), + status: Some(status), requested_partitions, total_jobs: total_jobs as u32, completed_jobs: completed_jobs as u32, @@ -220,6 +229,7 @@ pub mod list_response_helpers { completed_at, duration_ms, cancelled, + comment, } } @@ -256,9 +266,8 @@ mod tests { #[test] fn test_build_request_status_conversions() { - let status = BuildRequestStatus::BuildRequestCompleted; - assert_eq!(status.to_display_string(), "completed"); - assert_eq!(BuildRequestStatus::from_display_string("completed"), Some(status)); + let status = BuildRequestStatusCode::BuildRequestCompleted.status(); + assert_eq!(status.name, "completed"); } #[test] @@ -276,7 +285,7 @@ mod tests { fn test_invalid_display_string() { assert_eq!(PartitionStatus::from_display_string("invalid"), None); assert_eq!(JobStatus::from_display_string("invalid"), None); - assert_eq!(BuildRequestStatus::from_display_string("invalid"), None); + assert_eq!(BuildRequestStatusCode::from_display_string("invalid"), None); assert_eq!(DepType::from_display_string("invalid"), None); } } \ No newline at end of file