This commit is contained in:
Stuart Axelbrooke 2025-08-20 23:34:37 -07:00
parent bfcf7cdfd2
commit 8ba4820654
23 changed files with 301 additions and 270 deletions

View file

@ -900,7 +900,7 @@ async fn handle_builds_command(matches: &ArgMatches, event_log_uri: &str) -> Res
} }
_ => { _ => {
println!("Build: {}", detail.build_request_id); 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!("Requested partitions: {}", detail.requested_partitions.len());
println!("Total jobs: {}", detail.total_jobs); println!("Total jobs: {}", detail.total_jobs);
println!("Completed jobs: {}", detail.completed_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()); println!("\nTimeline ({} events):", detail.timeline.len());
for event in detail.timeline { for event in detail.timeline {
let timestamp = format_timestamp(event.timestamp); let timestamp = format_timestamp(event.timestamp);
let status_info = if let Some(ref status_name) = event.status_name { let status_info = event.status.unwrap().name;
format!(" -> {}", status_name)
} else {
String::new()
};
println!(" {} [{}]{} {}", timestamp, event.event_type, status_info, event.message); println!(" {} [{}]{} {}", timestamp, event.event_type, status_info, event.message);
if let Some(ref reason) = event.cancel_reason { if let Some(ref reason) = event.cancel_reason {

View file

@ -44,6 +44,7 @@ genrule(
"typescript_generated/src/models/BuildRequest.ts", "typescript_generated/src/models/BuildRequest.ts",
"typescript_generated/src/models/BuildRequestResponse.ts", "typescript_generated/src/models/BuildRequestResponse.ts",
"typescript_generated/src/models/BuildSummary.ts", "typescript_generated/src/models/BuildSummary.ts",
"typescript_generated/src/models/BuildRequestStatus.ts",
"typescript_generated/src/models/BuildTimelineEvent.ts", "typescript_generated/src/models/BuildTimelineEvent.ts",
"typescript_generated/src/models/BuildsListApiResponse.ts", "typescript_generated/src/models/BuildsListApiResponse.ts",
"typescript_generated/src/models/BuildsListResponse.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/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/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/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/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/BuildsListApiResponse.ts $(location typescript_generated/src/models/BuildsListApiResponse.ts)
cp $$TEMP_DIR/src/models/BuildsListResponse.ts $(location typescript_generated/src/models/BuildsListResponse.ts) cp $$TEMP_DIR/src/models/BuildsListResponse.ts $(location typescript_generated/src/models/BuildsListResponse.ts)

View file

@ -220,8 +220,8 @@ export const RecentActivity: TypedComponent<RecentActivityAttrs> = {
}, build.build_request_id) }, build.build_request_id)
]), ]),
m('td', [ m('td', [
// KEY FIX: build.status_name is now always a string, prevents runtime errors // KEY FIX: build.status.name is now always a string, prevents runtime errors
m(BuildStatusBadge, { status: build.status_name }) m(BuildStatusBadge, { status: build.status.name })
]), ]),
m('td.text-sm.opacity-70', formatTime(build.requested_at)), m('td.text-sm.opacity-70', formatTime(build.requested_at)),
]) ])
@ -276,7 +276,7 @@ export const RecentActivity: TypedComponent<RecentActivityAttrs> = {
}, partition.partition_ref.str) }, partition.partition_ref.str)
]), ]),
m('td', [ 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(PartitionStatusBadge, { status: partition.status_name })
]), ]),
m('td.text-sm.opacity-70', m('td.text-sm.opacity-70',
@ -402,8 +402,8 @@ export const BuildStatus: TypedComponent<BuildStatusAttrs> = {
startPolling() { startPolling() {
// Use different poll intervals based on build status // Use different poll intervals based on build status
const isActive = this.data?.status_name === 'EXECUTING' || const isActive = this.data?.status.name === 'EXECUTING' ||
this.data?.status_name === 'PLANNING'; this.data?.status.name === 'PLANNING';
const interval = isActive ? 2000 : 10000; // 2s for active, 10s for completed const interval = isActive ? 2000 : 10000; // 2s for active, 10s for completed
pollingManager.startPolling(`build-status-${this.buildId}`, () => { pollingManager.startPolling(`build-status-${this.buildId}`, () => {
@ -457,7 +457,7 @@ export const BuildStatus: TypedComponent<BuildStatusAttrs> = {
{stage: 'Build Requested', time: build.requested_at, icon: '🕚'}, {stage: 'Build Requested', time: build.requested_at, icon: '🕚'},
...(build.started_at ? [{stage: 'Build Started', time: build.started_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) => ({ // ...(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: '✅'}] : []), ...(build.completed_at ? [{stage: 'Build Completed', time: build.completed_at, icon: '✅'}] : []),
]; ];
@ -472,7 +472,7 @@ export const BuildStatus: TypedComponent<BuildStatusAttrs> = {
m('.stat.bg-base-100.shadow.rounded-lg.p-4', [ m('.stat.bg-base-100.shadow.rounded-lg.p-4', [
m('.stat-title', 'Status'), m('.stat-title', 'Status'),
m('.stat-value.text-2xl', [ 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', [ m('.stat.bg-base-100.shadow.rounded-lg.p-4', [
@ -518,7 +518,7 @@ export const BuildStatus: TypedComponent<BuildStatusAttrs> = {
m('.partition-status.flex.justify-between.items-center', [ m('.partition-status.flex.justify-between.items-center', [
// CLEAN: Always string status, no nested object access // CLEAN: Always string status, no nested object access
m(PartitionStatusBadge, { m(PartitionStatusBadge, {
status: partitionStatus?.status_name || 'Loading...', status: partitionStatus?.status.name || 'Loading...',
size: 'sm' size: 'sm'
}), }),
partitionStatus?.last_updated ? partitionStatus?.last_updated ?
@ -905,15 +905,15 @@ export const PartitionStatus: TypedComponent<PartitionStatusAttrs> = {
// Update status based on event type // Update status based on event type
if (event.eventType === 'build_request') { if (event.eventType === 'build_request') {
if (event.message?.includes('completed') || event.message?.includes('successful')) { if (event.message?.includes('completed') || event.message?.includes('successful')) {
build.status_name = 'Completed'; build.status.name = 'Completed';
build.completedAt = event.timestamp; build.completedAt = event.timestamp;
} else if (event.message?.includes('failed') || event.message?.includes('error')) { } else if (event.message?.includes('failed') || event.message?.includes('error')) {
build.status_name = 'Failed'; build.status.name = 'Failed';
build.completedAt = event.timestamp; build.completedAt = event.timestamp;
} else if (event.message?.includes('executing') || event.message?.includes('running')) { } else if (event.message?.includes('executing') || event.message?.includes('running')) {
build.status_name = 'Executing'; build.status.name = 'Executing';
} else if (event.message?.includes('planning')) { } else if (event.message?.includes('planning')) {
build.status_name = 'Planning'; build.status.name = 'Planning';
} }
} }
} }
@ -1012,7 +1012,7 @@ export const PartitionStatus: TypedComponent<PartitionStatusAttrs> = {
]), ]),
m('div.partition-meta.flex.gap-4.items-center.mb-4', [ 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 ? this.data?.last_updated ?
m('.timestamp.text-sm.opacity-70', m('.timestamp.text-sm.opacity-70',
`Last updated: ${formatDateTime(this.data.last_updated)}`) : null, `Last updated: ${formatDateTime(this.data.last_updated)}`) : null,
@ -1051,7 +1051,7 @@ export const PartitionStatus: TypedComponent<PartitionStatusAttrs> = {
}, build.id) }, build.id)
]), ]),
m('td', [ m('td', [
m(BuildStatusBadge, { status: build.status_name }) m(BuildStatusBadge, { status: build.status.name })
]), ]),
m('td.text-sm.opacity-70', m('td.text-sm.opacity-70',
formatDateTime(build.startedAt)), formatDateTime(build.startedAt)),

View file

@ -38,8 +38,7 @@ const apiClient = new DefaultApi(apiConfig);
function transformBuildSummary(apiResponse: BuildSummary): DashboardBuild { function transformBuildSummary(apiResponse: BuildSummary): DashboardBuild {
return { return {
build_request_id: apiResponse.build_request_id, build_request_id: apiResponse.build_request_id,
status_code: apiResponse.status_code, status: apiResponse.status!,
status_name: apiResponse.status_name,
requested_partitions: apiResponse.requested_partitions, // Keep as PartitionRef array requested_partitions: apiResponse.requested_partitions, // Keep as PartitionRef array
total_jobs: apiResponse.total_jobs, total_jobs: apiResponse.total_jobs,
completed_jobs: apiResponse.completed_jobs, completed_jobs: apiResponse.completed_jobs,
@ -56,8 +55,7 @@ function transformBuildSummary(apiResponse: BuildSummary): DashboardBuild {
function transformBuildDetail(apiResponse: BuildDetailResponse): DashboardBuild { function transformBuildDetail(apiResponse: BuildDetailResponse): DashboardBuild {
return { return {
build_request_id: apiResponse.build_request_id, build_request_id: apiResponse.build_request_id,
status_code: apiResponse.status_code, status: apiResponse.status!,
status_name: apiResponse.status_name,
requested_partitions: apiResponse.requested_partitions, // Keep as PartitionRef array requested_partitions: apiResponse.requested_partitions, // Keep as PartitionRef array
total_jobs: apiResponse.total_jobs, total_jobs: apiResponse.total_jobs,
completed_jobs: apiResponse.completed_jobs, completed_jobs: apiResponse.completed_jobs,

View file

@ -7,7 +7,8 @@ import {
BuildDetailResponse, BuildDetailResponse,
PartitionSummary, PartitionSummary,
JobSummary, JobSummary,
ActivityResponse ActivityResponse,
BuildRequestStatus
} from '../client/typescript_generated/src/index'; } from '../client/typescript_generated/src/index';
// Import types directly since we're now in the same ts_project // Import types directly since we're now in the same ts_project
@ -26,8 +27,7 @@ import {
function transformBuildSummary(apiResponse: BuildSummary): DashboardBuild { function transformBuildSummary(apiResponse: BuildSummary): DashboardBuild {
return { return {
build_request_id: apiResponse.build_request_id, build_request_id: apiResponse.build_request_id,
status_code: apiResponse.status_code, status: apiResponse.status!,
status_name: apiResponse.status_name,
requested_partitions: apiResponse.requested_partitions, // Keep as PartitionRef array requested_partitions: apiResponse.requested_partitions, // Keep as PartitionRef array
total_jobs: apiResponse.total_jobs, total_jobs: apiResponse.total_jobs,
completed_jobs: apiResponse.completed_jobs, completed_jobs: apiResponse.completed_jobs,
@ -44,8 +44,7 @@ function transformBuildSummary(apiResponse: BuildSummary): DashboardBuild {
function transformBuildDetail(apiResponse: BuildDetailResponse): DashboardBuild { function transformBuildDetail(apiResponse: BuildDetailResponse): DashboardBuild {
return { return {
build_request_id: apiResponse.build_request_id, build_request_id: apiResponse.build_request_id,
status_code: apiResponse.status_code, status: apiResponse.status!,
status_name: apiResponse.status_name,
requested_partitions: apiResponse.requested_partitions, // Keep as PartitionRef array requested_partitions: apiResponse.requested_partitions, // Keep as PartitionRef array
total_jobs: apiResponse.total_jobs, total_jobs: apiResponse.total_jobs,
completed_jobs: apiResponse.completed_jobs, completed_jobs: apiResponse.completed_jobs,
@ -98,8 +97,7 @@ function transformActivityResponse(apiResponse: ActivityResponse): DashboardActi
// Test Data Mocks // Test Data Mocks
const mockBuildSummary: BuildSummary = { const mockBuildSummary: BuildSummary = {
build_request_id: 'build-123', build_request_id: 'build-123',
status_code: 4, // BUILD_REQUEST_COMPLETED status: {code: 4, name: 'COMPLETED'},
status_name: 'COMPLETED',
requested_partitions: [{ str: 'partition-1' }, { str: 'partition-2' }], requested_partitions: [{ str: 'partition-1' }, { str: 'partition-2' }],
total_jobs: 5, total_jobs: 5,
completed_jobs: 5, completed_jobs: 5,
@ -151,12 +149,12 @@ o.spec('Transformation Functions', () => {
const result = transformBuildSummary(mockBuildSummary); const result = transformBuildSummary(mockBuildSummary);
// The key fix: status_name should be a string, status_code a number // The key fix: status_name should be a string, status_code a number
o(typeof result.status_code).equals('number'); o(typeof result.status?.code).equals('number');
o(typeof result.status_name).equals('string'); o(typeof result.status?.name).equals('string');
o(result.status_name).equals('COMPLETED'); o(result.status.name).equals('COMPLETED');
// This should not throw (preventing the original runtime error) // 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', () => { o('transformBuildSummary handles null optional fields', () => {
@ -219,7 +217,7 @@ o.spec('Transformation Functions', () => {
// All nested objects should be properly transformed // All nested objects should be properly transformed
o(result.recent_builds.length).equals(1); 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(result.recent_partitions.length).equals(1);
o(typeof result.recent_partitions[0]?.partition_ref).equals('object'); 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 // 1. status_name.toLowerCase() - should not crash
result.recent_builds.forEach((build: DashboardBuild) => { result.recent_builds.forEach((build: DashboardBuild) => {
o(() => build.status_name.toLowerCase()).notThrows('build.status_name.toLowerCase should work'); o(() => build.status.name.toLowerCase()).notThrows('build.status.name.toLowerCase should work');
o(build.status_name.toLowerCase()).equals('completed'); o(build.status.name.toLowerCase()).equals('completed');
}); });
// 2. partition_ref.str access - should access string property // 2. partition_ref.str access - should access string property

View file

@ -11,7 +11,8 @@ import {
JobMetricsResponse, JobMetricsResponse,
JobDailyStats, JobDailyStats,
JobRunSummary, JobRunSummary,
PartitionRef PartitionRef,
BuildRequestStatus
} from '../client/typescript_generated/src/index'; } from '../client/typescript_generated/src/index';
// Dashboard-optimized types - canonical frontend types independent of backend schema // Dashboard-optimized types - canonical frontend types independent of backend schema
@ -19,8 +20,7 @@ import {
export interface DashboardBuild { export interface DashboardBuild {
build_request_id: string; build_request_id: string;
status_code: number; status: BuildRequestStatus;
status_name: string;
requested_partitions: PartitionRef[]; requested_partitions: PartitionRef[];
total_jobs: number; total_jobs: number;
completed_jobs: number; completed_jobs: number;
@ -66,8 +66,7 @@ export interface DashboardActivity {
// Dashboard timeline event types for consistent UI handling // Dashboard timeline event types for consistent UI handling
export interface DashboardBuildTimelineEvent { export interface DashboardBuildTimelineEvent {
timestamp: number; timestamp: number;
status_code: number; status: BuildRequestStatus;
status_name: string;
message: string; message: string;
event_type: string; event_type: string;
cancel_reason?: string; cancel_reason?: string;
@ -75,8 +74,7 @@ export interface DashboardBuildTimelineEvent {
export interface DashboardPartitionTimelineEvent { export interface DashboardPartitionTimelineEvent {
timestamp: number; timestamp: number;
status_code: number; status: BuildRequestStatus;
status_name: string;
message: string; message: string;
build_request_id: string; build_request_id: string;
job_run_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 { export function isDashboardBuild(data: any): data is DashboardBuild {
return data && return data &&
typeof data.build_request_id === 'string' && typeof data.build_request_id === 'string' &&
typeof data.status_code === 'number' && typeof data.status?.code === 'number' &&
typeof data.status_name === 'string' && typeof data.status?.name === 'string' &&
typeof data.requested_at === 'number' && typeof data.requested_at === 'number' &&
Array.isArray(data.requested_partitions); Array.isArray(data.requested_partitions);
} }

View file

@ -239,9 +239,9 @@ message BuildRequestEvent {
// Optional status message // Optional status message
string message = 4; string message = 4;
// The comment attached to the request - contains arbitrary text // 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 // The id of the want that triggered this build
string want_id = 6; optional string want_id = 6;
} }
// Partition state change event // Partition state change event
@ -301,6 +301,7 @@ message WantEvent {
string want_id = 2; string want_id = 2;
// How this want was created // How this want was created
WantSource source = 3; WantSource source = 3;
string comment = 4;
} }
message PartitionWant { message PartitionWant {
@ -362,6 +363,7 @@ message BuildEvent {
// Event metadata // Event metadata
string event_id = 1; // UUID for this event string event_id = 1; // UUID for this event
int64 timestamp = 2; // Unix timestamp (nanoseconds) int64 timestamp = 2; // Unix timestamp (nanoseconds)
optional string build_request_id = 3;
// Event type and payload (one of) // Event type and payload (one of)
oneof event_type { oneof event_type {
@ -530,19 +532,18 @@ message BuildsListResponse {
message BuildSummary { message BuildSummary {
string build_request_id = 1; string build_request_id = 1;
BuildRequestStatusCode status_code = 2; // Enum for programmatic use BuildRequestStatus status = 2;
string status_name = 3; // Human-readable string repeated PartitionRef requested_partitions = 3;
repeated PartitionRef requested_partitions = 4; uint32 total_jobs = 4;
uint32 total_jobs = 5; uint32 completed_jobs = 5;
uint32 completed_jobs = 6; uint32 failed_jobs = 6;
uint32 failed_jobs = 7; uint32 cancelled_jobs = 7;
uint32 cancelled_jobs = 8; int64 requested_at = 8;
int64 requested_at = 9; optional int64 started_at = 9;
optional int64 started_at = 10; optional int64 completed_at = 10;
optional int64 completed_at = 11; optional int64 duration_ms = 11;
optional int64 duration_ms = 12; bool cancelled = 12;
bool cancelled = 13; optional string comment = 13;
string comment = 14;
} }
// //
@ -572,29 +573,27 @@ message BuildDetailRequest {
message BuildDetailResponse { message BuildDetailResponse {
string build_request_id = 1; string build_request_id = 1;
BuildRequestStatusCode status_code = 2; // Enum for programmatic use BuildRequestStatus status = 2;
string status_name = 3; // Human-readable string repeated PartitionRef requested_partitions = 3;
repeated PartitionRef requested_partitions = 4; uint32 total_jobs = 4;
uint32 total_jobs = 5; uint32 completed_jobs = 5;
uint32 completed_jobs = 6; uint32 failed_jobs = 6;
uint32 failed_jobs = 7; uint32 cancelled_jobs = 7;
uint32 cancelled_jobs = 8; int64 requested_at = 8;
int64 requested_at = 9; optional int64 started_at = 9;
optional int64 started_at = 10; optional int64 completed_at = 10;
optional int64 completed_at = 11; optional int64 duration_ms = 11;
optional int64 duration_ms = 12; bool cancelled = 12;
bool cancelled = 13; optional string cancel_reason = 13;
optional string cancel_reason = 14; repeated BuildTimelineEvent timeline = 14;
repeated BuildTimelineEvent timeline = 15;
} }
message BuildTimelineEvent { message BuildTimelineEvent {
int64 timestamp = 1; int64 timestamp = 1;
optional BuildRequestStatusCode status_code = 2; // Enum for programmatic use optional BuildRequestStatus status = 2;
optional string status_name = 3; // Human-readable string string message = 3;
string message = 4; string event_type = 4;
string event_type = 5; optional string cancel_reason = 5;
optional string cancel_reason = 6;
} }
// //

View file

@ -204,6 +204,8 @@ impl MockBuildEventLog {
Some(crate::build_event::EventType::PartitionInvalidationEvent(_)) => "partition_invalidation", Some(crate::build_event::EventType::PartitionInvalidationEvent(_)) => "partition_invalidation",
Some(crate::build_event::EventType::JobRunCancelEvent(_)) => "job_run_cancel", Some(crate::build_event::EventType::JobRunCancelEvent(_)) => "job_run_cancel",
Some(crate::build_event::EventType::BuildCancelEvent(_)) => "build_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", None => "unknown",
}, },
event_data event_data
@ -220,7 +222,7 @@ impl MockBuildEventLog {
"INSERT INTO build_request_events (event_id, status, requested_partitions, message) VALUES (?1, ?2, ?3, ?4)", "INSERT INTO build_request_events (event_id, status, requested_partitions, message) VALUES (?1, ?2, ?3, ?4)",
rusqlite::params![ rusqlite::params![
event.event_id, event.event_id,
br_event.status_code.to_string(), br_event.clone().status.unwrap().code.to_string(),
partitions_json, partitions_json,
br_event.message br_event.message
], ],
@ -379,12 +381,13 @@ pub mod test_events {
BuildEvent { BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), 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 { event_type: Some(build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: BuildRequestStatus::BuildRequestReceived as i32, status: Some(BuildRequestStatusCode::BuildRequestReceived.status()),
status_name: BuildRequestStatus::BuildRequestReceived.to_display_string(),
requested_partitions: partitions, requested_partitions: partitions,
message: "Build request received".to_string(), message: "Build request received".to_string(),
comment: None,
want_id: None,
})), })),
} }
} }
@ -398,12 +401,13 @@ pub mod test_events {
BuildEvent { BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), 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 { event_type: Some(build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: status as i32, status: Some(status.clone()),
status_name: status.to_display_string(),
requested_partitions: partitions, 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 { BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), 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 { event_type: Some(build_event::EventType::PartitionEvent(PartitionEvent {
partition_ref: Some(partition_ref), partition_ref: Some(partition_ref),
status_code: status as i32, status_code: status as i32,
@ -440,7 +444,7 @@ pub mod test_events {
BuildEvent { BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), 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 { event_type: Some(build_event::EventType::JobEvent(JobEvent {
job_run_id: job_run_id.unwrap_or_else(|| Uuid::new_v4().to_string()), job_run_id: job_run_id.unwrap_or_else(|| Uuid::new_v4().to_string()),
job_label: Some(job_label), job_label: Some(job_label),
@ -563,9 +567,10 @@ impl BELStorage for MockBELStorage {
// Apply filtering based on EventFilter // Apply filtering based on EventFilter
events.retain(|event| { events.retain(|event| {
// Filter by build request IDs if specified // Filter by build request IDs if specified
if !filter.build_request_ids.is_empty() { 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; return false;
} }
} }

View file

@ -85,7 +85,7 @@ pub fn create_build_event(
BuildEvent { BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), timestamp: current_timestamp_nanos(),
build_request_id, build_request_id: Some(build_request_id.clone()),
event_type: Some(event_type), event_type: Some(event_type),
} }
} }

View file

@ -38,15 +38,16 @@ impl BELQueryEngine {
}; };
let events = self.storage.list_events(0, filter).await?; let events = self.storage.list_events(0, filter).await?;
let mut active_builds = Vec::new(); let mut active_builds: Vec<String> = Vec::new();
let mut build_states: HashMap<String, BuildRequestStatus> = HashMap::new(); let mut build_states: HashMap<String, BuildRequestStatusCode> = HashMap::new();
// Process events chronologically to track build states // Process events chronologically to track build states
for event in events.events { for event in events.events {
let build_request_id = event.build_request_id.clone().unwrap();
match &event.event_type { match &event.event_type {
Some(crate::build_event::EventType::BuildRequestEvent(br_event)) => { Some(crate::build_event::EventType::BuildRequestEvent(br_event)) => {
if let Ok(status) = BuildRequestStatus::try_from(br_event.status_code) { if let Ok(code) = BuildRequestStatusCode::try_from(br_event.clone().status.unwrap().code) {
build_states.insert(event.build_request_id.clone(), status); build_states.insert(build_request_id.clone(), code);
} }
} }
Some(crate::build_event::EventType::PartitionEvent(p_event)) => { 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 let Ok(status) = PartitionStatus::try_from(p_event.status_code) {
if matches!(status, PartitionStatus::PartitionBuilding | PartitionStatus::PartitionAnalyzed) { if matches!(status, PartitionStatus::PartitionBuilding | PartitionStatus::PartitionAnalyzed) {
// Check if the build request is still active // 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, if matches!(build_status,
BuildRequestStatus::BuildRequestReceived | BuildRequestStatusCode::BuildRequestReceived |
BuildRequestStatus::BuildRequestPlanning | BuildRequestStatusCode::BuildRequestPlanning |
BuildRequestStatus::BuildRequestExecuting | BuildRequestStatusCode::BuildRequestExecuting |
BuildRequestStatus::BuildRequestAnalysisCompleted BuildRequestStatusCode::BuildRequestAnalysisCompleted
) { ) {
if !active_builds.contains(&event.build_request_id) { if !active_builds.contains(&build_request_id) {
active_builds.push(event.build_request_id.clone()); active_builds.push(build_request_id.clone());
} }
} }
} }
@ -97,12 +98,12 @@ impl BELQueryEngine {
return Err(BuildEventLogError::QueryError(format!("Build request '{}' not found", build_id))); 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 requested_partitions = Vec::new();
let mut created_at = 0i64; let mut created_at = 0i64;
let mut updated_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 event.timestamp > 0 {
if created_at == 0 || event.timestamp < created_at { if created_at == 0 || event.timestamp < created_at {
created_at = event.timestamp; 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 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; status = event_status;
} }
if !br_event.requested_partitions.is_empty() { if !br_event.requested_partitions.is_empty() {
@ -148,20 +149,20 @@ impl BELQueryEngine {
let mut build_summaries: HashMap<String, BuildRequestSummary> = HashMap::new(); let mut build_summaries: HashMap<String, BuildRequestSummary> = HashMap::new();
// Aggregate by build request ID // 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 { 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(|| { let entry = build_summaries.entry(build_id.clone()).or_insert_with(|| {
BuildRequestSummary { BuildRequestSummary {
build_request_id: build_id.clone(), build_request_id: build_id.clone(),
status: BuildRequestStatus::BuildRequestUnknown, status: BuildRequestStatusCode::BuildRequestUnknown.status(),
requested_partitions: Vec::new(), requested_partitions: Vec::new(),
created_at: event.timestamp, created_at: event.timestamp,
updated_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.status = status;
} }
entry.updated_at = event.timestamp.max(entry.updated_at); entry.updated_at = event.timestamp.max(entry.updated_at);
@ -179,8 +180,8 @@ impl BELQueryEngine {
// Apply status filter if provided // Apply status filter if provided
if let Some(status_filter) = &request.status_filter { if let Some(status_filter) = &request.status_filter {
if let Ok(filter_status) = status_filter.parse::<i32>() { if let Ok(filter_status) = status_filter.parse::<i32>() {
if let Ok(status) = BuildRequestStatus::try_from(filter_status) { if let Ok(status) = BuildRequestStatusCode::try_from(filter_status) {
builds.retain(|b| b.status == status); builds.retain(|b| b.status.code == status as i32);
} }
} }
} }
@ -194,8 +195,7 @@ impl BELQueryEngine {
.take(limit) .take(limit)
.map(|summary| BuildSummary { .map(|summary| BuildSummary {
build_request_id: summary.build_request_id, build_request_id: summary.build_request_id,
status_code: summary.status as i32, status: Some(summary.status),
status_name: summary.status.to_display_string(),
requested_partitions: summary.requested_partitions.into_iter() requested_partitions: summary.requested_partitions.into_iter()
.map(|s| PartitionRef { str: s }) .map(|s| PartitionRef { str: s })
.collect(), .collect(),
@ -208,6 +208,7 @@ impl BELQueryEngine {
completed_at: None, // TODO: Implement completed_at: None, // TODO: Implement
duration_ms: None, // TODO: Implement duration_ms: None, // TODO: Implement
cancelled: false, // TODO: Implement cancelled: false, // TODO: Implement
comment: None,
}) })
.collect(); .collect();
@ -228,18 +229,18 @@ impl BELQueryEngine {
let active_builds_count = builds_response.builds.iter() let active_builds_count = builds_response.builds.iter()
.filter(|b| matches!( .filter(|b| matches!(
BuildRequestStatus::try_from(b.status_code).unwrap_or(BuildRequestStatus::BuildRequestUnknown), BuildRequestStatusCode::try_from(b.status.clone().unwrap().code).unwrap_or(BuildRequestStatusCode::BuildRequestUnknown),
BuildRequestStatus::BuildRequestReceived | BuildRequestStatusCode::BuildRequestReceived |
BuildRequestStatus::BuildRequestPlanning | BuildRequestStatusCode::BuildRequestPlanning |
BuildRequestStatus::BuildRequestExecuting | BuildRequestStatusCode::BuildRequestExecuting |
BuildRequestStatus::BuildRequestAnalysisCompleted BuildRequestStatusCode::BuildRequestAnalysisCompleted
)) ))
.count() as u32; .count() as u32;
let recent_builds = builds_response.builds.into_iter() let recent_builds = builds_response.builds.into_iter()
.map(|b| BuildRequestSummary { .map(|b| BuildRequestSummary {
build_request_id: b.build_request_id, 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(), requested_partitions: b.requested_partitions.into_iter().map(|p| p.str).collect(),
created_at: b.requested_at, created_at: b.requested_at,
updated_at: b.completed_at.unwrap_or(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 partition_event_ref.str == partition_ref {
if let Ok(status) = PartitionStatus::try_from(p_event.status_code) { if let Ok(status) = PartitionStatus::try_from(p_event.status_code) {
if status == PartitionStatus::PartitionAvailable && event.timestamp >= latest_timestamp { 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; latest_timestamp = event.timestamp;
} }
} }

View file

@ -35,10 +35,11 @@ impl EventWriter {
let event = create_build_event( let event = create_build_event(
build_request_id, build_request_id,
build_event::EventType::BuildRequestEvent(BuildRequestEvent { build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: BuildRequestStatus::BuildRequestReceived as i32, status: Some(BuildRequestStatusCode::BuildRequestReceived.status()),
status_name: BuildRequestStatus::BuildRequestReceived.to_display_string(),
requested_partitions, requested_partitions,
message: "Build request received".to_string(), message: "Build request received".to_string(),
comment: None,
want_id: None,
}), }),
); );
@ -57,10 +58,11 @@ impl EventWriter {
let event = create_build_event( let event = create_build_event(
build_request_id, build_request_id,
build_event::EventType::BuildRequestEvent(BuildRequestEvent { build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: status as i32, status: Some(status),
status_name: status.to_display_string(),
requested_partitions: vec![], requested_partitions: vec![],
message, message,
comment: None,
want_id: None,
}), }),
); );
@ -80,10 +82,11 @@ impl EventWriter {
let event = create_build_event( let event = create_build_event(
build_request_id, build_request_id,
build_event::EventType::BuildRequestEvent(BuildRequestEvent { build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: status as i32, status: Some(status),
status_name: status.to_display_string(),
requested_partitions, requested_partitions,
message, message,
comment: None,
want_id: None,
}), }),
); );
@ -104,7 +107,7 @@ impl EventWriter {
let event = BuildEvent { let event = BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), timestamp: current_timestamp_nanos(),
build_request_id, build_request_id: Some(build_request_id),
event_type: Some(build_event::EventType::PartitionEvent(PartitionEvent { event_type: Some(build_event::EventType::PartitionEvent(PartitionEvent {
partition_ref: Some(partition_ref), partition_ref: Some(partition_ref),
status_code: status as i32, status_code: status as i32,
@ -136,7 +139,7 @@ impl EventWriter {
let event = BuildEvent { let event = BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), timestamp: current_timestamp_nanos(),
build_request_id, build_request_id: Some(build_request_id),
event_type: Some(build_event::EventType::PartitionInvalidationEvent( event_type: Some(build_event::EventType::PartitionInvalidationEvent(
PartitionInvalidationEvent { PartitionInvalidationEvent {
partition_ref: Some(partition_ref), partition_ref: Some(partition_ref),
@ -162,7 +165,7 @@ impl EventWriter {
let event = BuildEvent { let event = BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), timestamp: current_timestamp_nanos(),
build_request_id, build_request_id: Some(build_request_id),
event_type: Some(build_event::EventType::JobEvent(JobEvent { event_type: Some(build_event::EventType::JobEvent(JobEvent {
job_run_id, job_run_id,
job_label: Some(job_label), job_label: Some(job_label),
@ -194,7 +197,7 @@ impl EventWriter {
let event = BuildEvent { let event = BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), timestamp: current_timestamp_nanos(),
build_request_id, build_request_id: Some(build_request_id),
event_type: Some(build_event::EventType::JobEvent(JobEvent { event_type: Some(build_event::EventType::JobEvent(JobEvent {
job_run_id, job_run_id,
job_label: Some(job_label), job_label: Some(job_label),
@ -256,7 +259,7 @@ impl EventWriter {
let event = BuildEvent { let event = BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), timestamp: current_timestamp_nanos(),
build_request_id, build_request_id: Some(build_request_id),
event_type: Some(build_event::EventType::JobRunCancelEvent(JobRunCancelEvent { event_type: Some(build_event::EventType::JobRunCancelEvent(JobRunCancelEvent {
job_run_id, job_run_id,
reason, reason,
@ -285,22 +288,22 @@ impl EventWriter {
let latest_status = build_events.iter() let latest_status = build_events.iter()
.rev() .rev()
.find_map(|e| match &e.event_type { .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, _ => None,
}); });
match latest_status { match latest_status {
Some(status) if status == BuildRequestStatus::BuildRequestCompleted as i32 => { Some(status) if status == BuildRequestStatusCode::BuildRequestCompleted as i32 => {
return Err(BuildEventLogError::QueryError( return Err(BuildEventLogError::QueryError(
format!("Cannot cancel completed build: {}", build_request_id) 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( return Err(BuildEventLogError::QueryError(
format!("Cannot cancel failed build: {}", build_request_id) 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( return Err(BuildEventLogError::QueryError(
format!("Build already cancelled: {}", build_request_id) format!("Build already cancelled: {}", build_request_id)
)); ));
@ -311,7 +314,7 @@ impl EventWriter {
let event = BuildEvent { let event = BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), 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 { event_type: Some(build_event::EventType::BuildCancelEvent(BuildCancelEvent {
reason, reason,
})), })),
@ -322,7 +325,7 @@ impl EventWriter {
// Also emit a build request status update // Also emit a build request status update
self.update_build_status( self.update_build_status(
build_request_id, build_request_id,
BuildRequestStatus::BuildRequestCancelled, BuildRequestStatusCode::BuildRequestCancelled.status(),
"Build cancelled by user".to_string(), "Build cancelled by user".to_string(),
).await ).await
} }
@ -361,7 +364,7 @@ impl EventWriter {
let event = BuildEvent { let event = BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), timestamp: current_timestamp_nanos(),
build_request_id, build_request_id: Some(build_request_id),
event_type: Some(build_event::EventType::JobGraphEvent(JobGraphEvent { event_type: Some(build_event::EventType::JobGraphEvent(JobGraphEvent {
job_graph: Some(job_graph), job_graph: Some(job_graph),
message, message,
@ -391,19 +394,19 @@ mod tests {
// Test status updates // Test status updates
writer.update_build_status( writer.update_build_status(
build_id.clone(), build_id.clone(),
BuildRequestStatus::BuildRequestPlanning, BuildRequestStatusCode::BuildRequestPlanning.status(),
"Starting planning".to_string(), "Starting planning".to_string(),
).await.unwrap(); ).await.unwrap();
writer.update_build_status( writer.update_build_status(
build_id.clone(), build_id.clone(),
BuildRequestStatus::BuildRequestExecuting, BuildRequestStatusCode::BuildRequestExecuting.status(),
"Starting execution".to_string(), "Starting execution".to_string(),
).await.unwrap(); ).await.unwrap();
writer.update_build_status( writer.update_build_status(
build_id.clone(), build_id.clone(),
BuildRequestStatus::BuildRequestCompleted, BuildRequestStatusCode::BuildRequestCompleted.status(),
"Build completed successfully".to_string(), "Build completed successfully".to_string(),
).await.unwrap(); ).await.unwrap();
} }

View file

@ -107,9 +107,8 @@ mod format_consistency_tests {
assert_eq!(JobStatus::from_display_string("completed"), Some(job_status)); assert_eq!(JobStatus::from_display_string("completed"), Some(job_status));
// Test BuildRequestStatus conversions // Test BuildRequestStatus conversions
let build_status = BuildRequestStatus::BuildRequestCompleted; let build_status = BuildRequestStatusCode::BuildRequestCompleted.status();
assert_eq!(build_status.to_display_string(), "completed"); assert_eq!(build_status.name, "completed");
assert_eq!(BuildRequestStatus::from_display_string("completed"), Some(build_status));
// Test invalid conversions // Test invalid conversions
assert_eq!(PartitionStatus::from_display_string("invalid"), None); assert_eq!(PartitionStatus::from_display_string("invalid"), None);

View file

@ -203,10 +203,11 @@ async fn plan(
let event = create_build_event( let event = create_build_event(
build_request_id.to_string(), build_request_id.to_string(),
crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent { crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: BuildRequestStatus::BuildRequestReceived as i32, status: Some(BuildRequestStatusCode::BuildRequestReceived.status()),
status_name: BuildRequestStatus::BuildRequestReceived.to_display_string(),
requested_partitions: output_refs.iter().map(|s| PartitionRef { str: s.clone() }).collect(), requested_partitions: output_refs.iter().map(|s| PartitionRef { str: s.clone() }).collect(),
message: "Analysis started".to_string(), message: "Analysis started".to_string(),
comment: None,
want_id: None,
}) })
); );
if let Err(e) = query_engine_ref.append_event(event).await { if let Err(e) = query_engine_ref.append_event(event).await {
@ -264,10 +265,11 @@ async fn plan(
let event = create_build_event( let event = create_build_event(
build_request_id.to_string(), build_request_id.to_string(),
crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent { crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: BuildRequestStatus::BuildRequestPlanning as i32, status: Some(BuildRequestStatusCode::BuildRequestPlanning.status()),
status_name: BuildRequestStatus::BuildRequestPlanning.to_display_string(),
requested_partitions: output_refs.iter().map(|s| PartitionRef { str: s.clone() }).collect(), requested_partitions: output_refs.iter().map(|s| PartitionRef { str: s.clone() }).collect(),
message: "Graph analysis in progress".to_string(), message: "Graph analysis in progress".to_string(),
comment: None,
want_id: None,
}) })
); );
if let Err(e) = query_engine_ref.append_event(event).await { if let Err(e) = query_engine_ref.append_event(event).await {
@ -334,10 +336,11 @@ async fn plan(
let event = create_build_event( let event = create_build_event(
build_request_id.to_string(), build_request_id.to_string(),
crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent { crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: BuildRequestStatus::BuildRequestAnalysisCompleted as i32, status: Some(BuildRequestStatusCode::BuildRequestAnalysisCompleted.status()),
status_name: BuildRequestStatus::BuildRequestAnalysisCompleted.to_display_string(),
requested_partitions: output_refs.iter().map(|s| PartitionRef { str: s.clone() }).collect(), requested_partitions: output_refs.iter().map(|s| PartitionRef { str: s.clone() }).collect(),
message: format!("Analysis completed successfully, {} tasks planned", nodes.len()), message: format!("Analysis completed successfully, {} tasks planned", nodes.len()),
comment: None,
want_id: None,
}) })
); );
if let Err(e) = query_engine.append_event(event).await { if let Err(e) = query_engine.append_event(event).await {
@ -376,10 +379,11 @@ async fn plan(
let event = create_build_event( let event = create_build_event(
build_request_id.to_string(), build_request_id.to_string(),
crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent { crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: BuildRequestStatus::BuildRequestFailed as i32, status: Some(BuildRequestStatusCode::BuildRequestFailed.status()),
status_name: BuildRequestStatus::BuildRequestFailed.to_display_string(),
requested_partitions: output_refs.iter().map(|s| PartitionRef { str: s.clone() }).collect(), requested_partitions: output_refs.iter().map(|s| PartitionRef { str: s.clone() }).collect(),
message: "No jobs found for requested partitions".to_string(), message: "No jobs found for requested partitions".to_string(),
comment: None,
want_id: None,
}) })
); );
if let Err(e) = query_engine.append_event(event).await { if let Err(e) = query_engine.append_event(event).await {

View file

@ -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::event_log::{create_bel_query_engine, create_build_event};
use databuild::build_event::EventType; use databuild::build_event::EventType;
use databuild::log_collector::{LogCollector, LogCollectorError}; use databuild::log_collector::{LogCollector, LogCollectorError};
@ -460,10 +460,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let event = create_build_event( let event = create_build_event(
build_request_id.clone(), build_request_id.clone(),
EventType::BuildRequestEvent(BuildRequestEvent { EventType::BuildRequestEvent(BuildRequestEvent {
status_code: BuildRequestStatus::BuildRequestExecuting as i32, status: Some(BuildRequestStatusCode::BuildRequestExecuting.status()),
status_name: BuildRequestStatus::BuildRequestExecuting.to_display_string(),
requested_partitions: graph.outputs.clone(), requested_partitions: graph.outputs.clone(),
message: format!("Starting execution of {} jobs", graph.nodes.len()), message: format!("Starting execution of {} jobs", graph.nodes.len()),
comment: None,
want_id: None,
}) })
); );
if let Err(e) = query_engine.append_event(event).await { if let Err(e) = query_engine.append_event(event).await {
@ -787,18 +788,19 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Log final build request status (existing detailed event) // Log final build request status (existing detailed event)
if let Some(ref query_engine) = build_event_log { if let Some(ref query_engine) = build_event_log {
let final_status = if failure_count > 0 || fail_fast_triggered { let final_status = if failure_count > 0 || fail_fast_triggered {
BuildRequestStatus::BuildRequestFailed BuildRequestStatusCode::BuildRequestFailed
} else { } else {
BuildRequestStatus::BuildRequestCompleted BuildRequestStatusCode::BuildRequestCompleted
}; };
let event = create_build_event( let event = create_build_event(
build_request_id.clone(), build_request_id.clone(),
EventType::BuildRequestEvent(BuildRequestEvent { EventType::BuildRequestEvent(BuildRequestEvent {
status_code: final_status as i32, status: Some(final_status.status()),
status_name: final_status.to_display_string(),
requested_partitions: graph.outputs.clone(), requested_partitions: graph.outputs.clone(),
message: format!("Execution completed: {} succeeded, {} failed", success_count, failure_count), 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 { if let Err(e) = query_engine.append_event(event).await {

View file

@ -10,10 +10,11 @@ pub fn create_build_request_received_event(
create_build_event( create_build_event(
build_request_id, build_request_id,
build_event::EventType::BuildRequestEvent(BuildRequestEvent { build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: BuildRequestStatus::BuildRequestReceived as i32, status: Some(BuildRequestStatusCode::BuildRequestReceived.status()),
status_name: BuildRequestStatus::BuildRequestReceived.to_display_string(),
requested_partitions, requested_partitions,
message: "Build request received".to_string(), 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( create_build_event(
build_request_id, build_request_id,
build_event::EventType::BuildRequestEvent(BuildRequestEvent { build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: BuildRequestStatus::BuildRequestPlanning as i32, status: Some(BuildRequestStatusCode::BuildRequestPlanning.status()),
status_name: BuildRequestStatus::BuildRequestPlanning.to_display_string(),
requested_partitions: vec![], requested_partitions: vec![],
message: "Starting build planning".to_string(), 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( create_build_event(
build_request_id, build_request_id,
build_event::EventType::BuildRequestEvent(BuildRequestEvent { build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: BuildRequestStatus::BuildRequestExecuting as i32, status: Some(BuildRequestStatusCode::BuildRequestExecuting.status()),
status_name: BuildRequestStatus::BuildRequestExecuting.to_display_string(),
requested_partitions: vec![], requested_partitions: vec![],
message: "Starting build execution".to_string(), 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 { let status = match result {
super::BuildResult::Success { .. } => BuildRequestStatus::BuildRequestCompleted, super::BuildResult::Success { .. } => BuildRequestStatusCode::BuildRequestCompleted.status(),
super::BuildResult::Failed { .. } | super::BuildResult::FailFast { .. } => BuildRequestStatus::BuildRequestFailed, super::BuildResult::Failed { .. } | super::BuildResult::FailFast { .. } => BuildRequestStatusCode::BuildRequestFailed.status(),
}; };
create_build_event( create_build_event(
build_request_id, build_request_id,
build_event::EventType::BuildRequestEvent(BuildRequestEvent { build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: status as i32, status: Some(status),
status_name: status.to_display_string(),
requested_partitions: vec![], requested_partitions: vec![],
message, message,
comment: None,
want_id: None,
}), }),
) )
} }
@ -86,10 +90,11 @@ pub fn create_analysis_completed_event(
create_build_event( create_build_event(
build_request_id, build_request_id,
build_event::EventType::BuildRequestEvent(BuildRequestEvent { build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: BuildRequestStatus::BuildRequestAnalysisCompleted as i32, status: Some(BuildRequestStatusCode::BuildRequestAnalysisCompleted.status()),
status_name: BuildRequestStatus::BuildRequestAnalysisCompleted.to_display_string(),
requested_partitions, requested_partitions,
message: format!("Analysis completed successfully, {} tasks planned", task_count), 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 { BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), timestamp: current_timestamp_nanos(),
build_request_id, build_request_id: Some(build_request_id),
event_type: Some(build_event::EventType::JobEvent(job_event.clone())), event_type: Some(build_event::EventType::JobEvent(job_event.clone())),
} }
} }
@ -113,7 +118,7 @@ pub fn create_job_completed_event(
BuildEvent { BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), timestamp: current_timestamp_nanos(),
build_request_id, build_request_id: Some(build_request_id),
event_type: Some(build_event::EventType::JobEvent(job_event.clone())), event_type: Some(build_event::EventType::JobEvent(job_event.clone())),
} }
} }
@ -125,7 +130,7 @@ pub fn create_partition_available_event(
BuildEvent { BuildEvent {
event_id: generate_event_id(), event_id: generate_event_id(),
timestamp: current_timestamp_nanos(), timestamp: current_timestamp_nanos(),
build_request_id, build_request_id: Some(build_request_id),
event_type: Some(build_event::EventType::PartitionEvent(partition_event.clone())), event_type: Some(build_event::EventType::PartitionEvent(partition_event.clone())),
} }
} }

View file

@ -66,7 +66,7 @@ impl BuildOrchestrator {
self.event_writer.update_build_status( self.event_writer.update_build_status(
self.build_request_id.clone(), self.build_request_id.clone(),
BuildRequestStatus::BuildRequestPlanning, BuildRequestStatusCode::BuildRequestPlanning.status(),
"Starting build planning".to_string(), "Starting build planning".to_string(),
).await ).await
.map_err(OrchestrationError::EventLog)?; .map_err(OrchestrationError::EventLog)?;
@ -80,7 +80,7 @@ impl BuildOrchestrator {
self.event_writer.update_build_status( self.event_writer.update_build_status(
self.build_request_id.clone(), self.build_request_id.clone(),
BuildRequestStatus::BuildRequestExecuting, BuildRequestStatusCode::BuildRequestExecuting.status(),
"Starting build execution".to_string(), "Starting build execution".to_string(),
).await ).await
.map_err(OrchestrationError::EventLog)?; .map_err(OrchestrationError::EventLog)?;
@ -95,22 +95,22 @@ impl BuildOrchestrator {
let (status, message) = match &result { let (status, message) = match &result {
BuildResult::Success { jobs_completed } => { BuildResult::Success { jobs_completed } => {
(BuildRequestStatus::BuildRequestCompleted, (BuildRequestStatusCode::BuildRequestCompleted,
format!("Build completed successfully with {} jobs", jobs_completed)) format!("Build completed successfully with {} jobs", jobs_completed))
} }
BuildResult::Failed { jobs_completed, jobs_failed } => { BuildResult::Failed { jobs_completed, jobs_failed } => {
(BuildRequestStatus::BuildRequestFailed, (BuildRequestStatusCode::BuildRequestFailed,
format!("Build failed: {} jobs completed, {} jobs failed", jobs_completed, jobs_failed)) format!("Build failed: {} jobs completed, {} jobs failed", jobs_completed, jobs_failed))
} }
BuildResult::FailFast { trigger_job } => { BuildResult::FailFast { trigger_job } => {
(BuildRequestStatus::BuildRequestFailed, (BuildRequestStatusCode::BuildRequestFailed,
format!("Build failed fast due to job: {}", trigger_job)) format!("Build failed fast due to job: {}", trigger_job))
} }
}; };
self.event_writer.update_build_status( self.event_writer.update_build_status(
self.build_request_id.clone(), self.build_request_id.clone(),
status, status.status(),
message, message,
).await ).await
.map_err(OrchestrationError::EventLog)?; .map_err(OrchestrationError::EventLog)?;
@ -122,7 +122,7 @@ impl BuildOrchestrator {
pub async fn emit_analysis_completed(&self, task_count: usize) -> Result<()> { pub async fn emit_analysis_completed(&self, task_count: usize) -> Result<()> {
self.event_writer.update_build_status_with_partitions( self.event_writer.update_build_status_with_partitions(
self.build_request_id.clone(), self.build_request_id.clone(),
BuildRequestStatus::BuildRequestAnalysisCompleted, BuildRequestStatusCode::BuildRequestAnalysisCompleted.status(),
self.requested_partitions.clone(), self.requested_partitions.clone(),
format!("Analysis completed successfully, {} tasks planned", task_count), format!("Analysis completed successfully, {} tasks planned", task_count),
).await ).await

View file

@ -62,7 +62,7 @@ impl BuildsRepository {
let builds = response.builds.into_iter().map(|build| { let builds = response.builds.into_iter().map(|build| {
BuildInfo { BuildInfo {
build_request_id: build.build_request_id, 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_partitions: build.requested_partitions,
requested_at: build.requested_at, requested_at: build.requested_at,
started_at: build.started_at, started_at: build.started_at,
@ -116,7 +116,7 @@ impl BuildsRepository {
let mut timeline = Vec::new(); let mut timeline = Vec::new();
for event in all_events { for event in all_events {
if let Some(crate::build_event::EventType::BuildRequestEvent(br_event)) = &event.event_type { 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 { timeline.push(BuildEvent {
timestamp: event.timestamp, timestamp: event.timestamp,
event_type: "build_status".to_string(), event_type: "build_status".to_string(),
@ -151,8 +151,7 @@ impl BuildsRepository {
.into_iter() .into_iter()
.map(|event| ServiceBuildTimelineEvent { .map(|event| ServiceBuildTimelineEvent {
timestamp: event.timestamp, timestamp: event.timestamp,
status_code: event.status.map(|s| s as i32), status: event.status,
status_name: event.status.map(|s| s.to_display_string()),
message: event.message, message: event.message,
event_type: event.event_type, event_type: event.event_type,
cancel_reason: event.cancel_reason, cancel_reason: event.cancel_reason,
@ -161,8 +160,7 @@ impl BuildsRepository {
let response = BuildDetailResponse { let response = BuildDetailResponse {
build_request_id: build_info.build_request_id, build_request_id: build_info.build_request_id,
status_code: build_info.status as i32, status: Some(build_info.status),
status_name: build_info.status.to_display_string(),
requested_partitions: build_info.requested_partitions, requested_partitions: build_info.requested_partitions,
total_jobs: build_info.total_jobs as u32, total_jobs: build_info.total_jobs as u32,
completed_jobs: build_info.completed_jobs as u32, completed_jobs: build_info.completed_jobs as u32,
@ -200,18 +198,18 @@ impl BuildsRepository {
let (build, _timeline) = build_info.unwrap(); let (build, _timeline) = build_info.unwrap();
// Check if build is in a cancellable state // Check if build is in a cancellable state
match build.status { match BuildRequestStatusCode::try_from(build.status.code) {
BuildRequestStatus::BuildRequestCompleted => { Ok(BuildRequestStatusCode::BuildRequestCompleted) => {
return Err(BuildEventLogError::QueryError( return Err(BuildEventLogError::QueryError(
format!("Cannot cancel completed build: {}", build_request_id) format!("Cannot cancel completed build: {}", build_request_id)
)); ));
} }
BuildRequestStatus::BuildRequestFailed => { Ok(BuildRequestStatusCode::BuildRequestFailed) => {
return Err(BuildEventLogError::QueryError( return Err(BuildEventLogError::QueryError(
format!("Cannot cancel failed build: {}", build_request_id) format!("Cannot cancel failed build: {}", build_request_id)
)); ));
} }
BuildRequestStatus::BuildRequestCancelled => { Ok(BuildRequestStatusCode::BuildRequestCancelled) => {
return Err(BuildEventLogError::QueryError( return Err(BuildEventLogError::QueryError(
format!("Build already cancelled: {}", build_request_id) format!("Build already cancelled: {}", build_request_id)
)); ));
@ -225,10 +223,11 @@ impl BuildsRepository {
let cancel_event = create_build_event( let cancel_event = create_build_event(
build_request_id.to_string(), build_request_id.to_string(),
crate::build_event::EventType::BuildRequestEvent(crate::BuildRequestEvent { crate::build_event::EventType::BuildRequestEvent(crate::BuildRequestEvent {
status_code: BuildRequestStatus::BuildRequestCancelled as i32, status: Some(BuildRequestStatusCode::BuildRequestCancelled.status()),
status_name: BuildRequestStatus::BuildRequestCancelled.to_display_string(),
requested_partitions: build.requested_partitions, requested_partitions: build.requested_partitions,
message: format!("Build cancelled"), message: format!("Build cancelled"),
comment: None,
want_id: None,
}) })
); );
@ -250,8 +249,7 @@ impl BuildsRepository {
.into_iter() .into_iter()
.map(|build| crate::BuildSummary { .map(|build| crate::BuildSummary {
build_request_id: build.build_request_id, build_request_id: build.build_request_id,
status_code: build.status as i32, status: Some(build.status),
status_name: build.status.to_display_string(),
requested_partitions: build.requested_partitions.into_iter().map(|p| crate::PartitionRef { str: p.str }).collect(), requested_partitions: build.requested_partitions.into_iter().map(|p| crate::PartitionRef { str: p.str }).collect(),
total_jobs: build.total_jobs as u32, total_jobs: build.total_jobs as u32,
completed_jobs: build.completed_jobs as u32, completed_jobs: build.completed_jobs as u32,
@ -262,6 +260,7 @@ impl BuildsRepository {
completed_at: build.completed_at, completed_at: build.completed_at,
duration_ms: build.duration_ms, duration_ms: build.duration_ms,
cancelled: build.cancelled, cancelled: build.cancelled,
comment: None,
}) })
.collect(); .collect();
@ -292,10 +291,10 @@ mod tests {
// Create events for multiple builds // Create events for multiple builds
let events = vec![ 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()], BuildRequestStatusCode::BuildRequestReceived.status()),
test_events::build_request_event(Some(build_id1.clone()), vec![partition1.clone()], BuildRequestStatus::BuildRequestCompleted), 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()], BuildRequestStatus::BuildRequestReceived), 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()], BuildRequestStatus::BuildRequestFailed), 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(); 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 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(); 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_eq!(build1.requested_partitions.len(), 1);
assert!(!build1.cancelled); assert!(!build1.cancelled);
assert_eq!(build2.status, BuildRequestStatus::BuildRequestFailed); assert_eq!(build2.status, BuildRequestStatusCode::BuildRequestFailed.status());
assert_eq!(build2.requested_partitions.len(), 1); assert_eq!(build2.requested_partitions.len(), 1);
assert!(!build2.cancelled); assert!(!build2.cancelled);
} }
@ -323,10 +322,10 @@ mod tests {
let partition = PartitionRef { str: "analytics/daily".to_string() }; let partition = PartitionRef { str: "analytics/daily".to_string() };
let events = vec![ 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()], BuildRequestStatusCode::BuildRequestReceived.status()),
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()], BuildRequestStatusCode::BuildRequestPlanning.status()),
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::BuildRequestExecuting.status()),
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::BuildRequestCompleted.status()),
]; ];
let query_engine = create_mock_bel_query_engine_with_events(events).await.unwrap(); let query_engine = create_mock_bel_query_engine_with_events(events).await.unwrap();
@ -337,14 +336,14 @@ mod tests {
let (info, timeline) = result.unwrap(); let (info, timeline) = result.unwrap();
assert_eq!(info.build_request_id, build_id); 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!(!info.cancelled);
assert_eq!(timeline.len(), 4); assert_eq!(timeline.len(), 4);
assert_eq!(timeline[0].status, Some(BuildRequestStatus::BuildRequestReceived)); assert_eq!(timeline[0].status, Some(BuildRequestStatusCode::BuildRequestReceived.status()));
assert_eq!(timeline[1].status, Some(BuildRequestStatus::BuildRequestPlanning)); assert_eq!(timeline[1].status, Some(BuildRequestStatusCode::BuildRequestPlanning.status()));
assert_eq!(timeline[2].status, Some(BuildRequestStatus::BuildRequestExecuting)); assert_eq!(timeline[2].status, Some(BuildRequestStatusCode::BuildRequestExecuting.status()));
assert_eq!(timeline[3].status, Some(BuildRequestStatus::BuildRequestCompleted)); assert_eq!(timeline[3].status, Some(BuildRequestStatusCode::BuildRequestCompleted.status()));
} }
#[tokio::test] #[tokio::test]
@ -363,8 +362,8 @@ mod tests {
// Start with a running build // Start with a running build
let events = vec![ 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()], BuildRequestStatusCode::BuildRequestReceived.status()),
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::BuildRequestExecuting.status()),
]; ];
let query_engine = create_mock_bel_query_engine_with_events(events).await.unwrap(); let query_engine = create_mock_bel_query_engine_with_events(events).await.unwrap();
@ -389,8 +388,8 @@ mod tests {
// Create a completed build // Create a completed build
let events = vec![ 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()], BuildRequestStatusCode::BuildRequestReceived.status()),
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::BuildRequestCompleted.status()),
]; ];
let query_engine = create_mock_bel_query_engine_with_events(events).await.unwrap(); let query_engine = create_mock_bel_query_engine_with_events(events).await.unwrap();

View file

@ -102,7 +102,7 @@ impl JobsRepository {
let job_run = JobRunDetail { let job_run = JobRunDetail {
job_run_id: j_event.job_run_id.clone(), job_run_id: j_event.job_run_id.clone(),
job_label: job_label.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(), target_partitions: j_event.target_partitions.clone(),
status, status,
scheduled_at: event.timestamp, scheduled_at: event.timestamp,
@ -229,7 +229,7 @@ impl JobsRepository {
let job_run = JobRunDetail { let job_run = JobRunDetail {
job_run_id: j_event.job_run_id.clone(), job_run_id: j_event.job_run_id.clone(),
job_label: job_label.to_string(), 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(), target_partitions: j_event.target_partitions.clone(),
status, status,
scheduled_at: event.timestamp, scheduled_at: event.timestamp,

View file

@ -164,7 +164,7 @@ impl PartitionsRepository {
timestamp: event.timestamp, timestamp: event.timestamp,
status: event_status, status: event_status,
message: p_event.message.clone(), 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()) }, job_run_id: if p_event.job_run_id.is_empty() { None } else { Some(p_event.job_run_id.clone()) },
}); });
} }

View file

@ -86,7 +86,7 @@ impl TasksRepository {
TaskInfo { TaskInfo {
job_run_id: j_event.job_run_id.clone(), job_run_id: j_event.job_run_id.clone(),
job_label: job_label.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, status: JobStatus::JobUnknown,
target_partitions: j_event.target_partitions.clone(), target_partitions: j_event.target_partitions.clone(),
scheduled_at: event.timestamp, scheduled_at: event.timestamp,
@ -182,7 +182,7 @@ impl TasksRepository {
task_info = Some(TaskInfo { task_info = Some(TaskInfo {
job_run_id: j_event.job_run_id.clone(), job_run_id: j_event.job_run_id.clone(),
job_label: job_label.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, status: JobStatus::JobUnknown,
target_partitions: j_event.target_partitions.clone(), target_partitions: j_event.target_partitions.clone(),
scheduled_at: event.timestamp, scheduled_at: event.timestamp,

View file

@ -61,7 +61,7 @@ pub async fn submit_build_request(
// Create build request state // Create build request state
let build_state = BuildRequestState { let build_state = BuildRequestState {
build_request_id: build_request_id.clone(), build_request_id: build_request_id.clone(),
status: BuildRequestStatus::BuildRequestReceived, status: BuildRequestStatusCode::BuildRequestReceived.status(),
requested_partitions: request.partitions.clone(), requested_partitions: request.partitions.clone(),
created_at: timestamp, created_at: timestamp,
updated_at: timestamp, updated_at: timestamp,
@ -160,7 +160,7 @@ pub async fn cancel_build_request(
{ {
let mut active_builds = service.active_builds.write().await; let mut active_builds = service.active_builds.write().await;
if let Some(build_state) = active_builds.get_mut(&build_request_id) { 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(); build_state.updated_at = current_timestamp_nanos();
} else { } else {
return Err(( return Err((
@ -176,10 +176,11 @@ pub async fn cancel_build_request(
let event = create_build_event( let event = create_build_event(
build_request_id.clone(), build_request_id.clone(),
crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent { crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent {
status_code: BuildRequestStatus::BuildRequestCancelled as i32, status: Some(BuildRequestStatusCode::BuildRequestCancelled.status()),
status_name: BuildRequestStatus::BuildRequestCancelled.to_display_string(),
requested_partitions: vec![], requested_partitions: vec![],
message: "Build request cancelled".to_string(), 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 decoded_partition_ref = base64_url_decode(&partition_ref).unwrap();
let events = match service.query_engine.get_partition_events(&decoded_partition_ref, None).await { 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); let (job_label, partition_ref, delegated_build_id) = extract_navigation_data(&e.event_type);
BuildEventSummary { BuildEventSummary {
event_id: e.event_id, event_id: e.event_id,
timestamp: e.timestamp, timestamp: e.timestamp,
event_type: event_type_to_string(&e.event_type), event_type: event_type_to_string(&e.event_type),
message: event_to_message(&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, job_label,
partition_ref, partition_ref,
delegated_build_id, delegated_build_id,
@ -350,7 +351,7 @@ async fn execute_build_request(
); );
// Update status to planning // 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 // Log planning event
if let Err(e) = orchestrator.start_planning().await { if let Err(e) = orchestrator.start_planning().await {
@ -362,7 +363,7 @@ async fn execute_build_request(
Ok(graph) => graph, Ok(graph) => graph,
Err(e) => { Err(e) => {
error!("Failed to analyze build graph: {}", 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 // Log failure event
if let Err(log_err) = orchestrator.complete_build(BuildResult::Failed { jobs_completed: 0, jobs_failed: 1 }).await { 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 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 // Log executing event
if let Err(e) = orchestrator.start_execution().await { 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 { match run_execute_command(&service, &build_request_id, &job_graph).await {
Ok(_) => { Ok(_) => {
info!("Build request {} completed successfully", build_request_id); 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 // Log completion event
if let Err(e) = orchestrator.complete_build(BuildResult::Success { jobs_completed: 0 }).await { if let Err(e) = orchestrator.complete_build(BuildResult::Success { jobs_completed: 0 }).await {
@ -397,7 +398,7 @@ async fn execute_build_request(
} }
Err(e) => { Err(e) => {
error!("Build request {} failed: {}", build_request_id, 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 // Log failure event
if let Err(log_err) = orchestrator.complete_build(BuildResult::Failed { jobs_completed: 0, jobs_failed: 1 }).await { 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<crate::build_event::EventType>) -> S
Some(crate::build_event::EventType::PartitionInvalidationEvent(_)) => "partition_invalidation".to_string(), 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::JobRunCancelEvent(_)) => "task_cancel".to_string(),
Some(crate::build_event::EventType::BuildCancelEvent(_)) => "build_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<crate::build_event::EventType>) -> Strin
Some(crate::build_event::EventType::PartitionInvalidationEvent(event)) => event.reason.clone(), 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::JobRunCancelEvent(event)) => event.reason.clone(),
Some(crate::build_event::EventType::BuildCancelEvent(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<crate::build_event::EventType>) -
// Build cancel events don't need navigation links // Build cancel events don't need navigation links
(None, None, None) (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), None => (None, None, None),
} }
} }
@ -1417,8 +1429,7 @@ pub async fn get_build_detail(
let timeline_events: Vec<BuildTimelineEvent> = protobuf_response.timeline.into_iter().map(|event| { let timeline_events: Vec<BuildTimelineEvent> = protobuf_response.timeline.into_iter().map(|event| {
BuildTimelineEvent { BuildTimelineEvent {
timestamp: event.timestamp, timestamp: event.timestamp,
status_code: event.status_code, status: event.status,
status_name: event.status_name,
message: event.message, message: event.message,
event_type: event.event_type, event_type: event.event_type,
cancel_reason: event.cancel_reason, cancel_reason: event.cancel_reason,
@ -1427,8 +1438,7 @@ pub async fn get_build_detail(
Ok(Json(BuildDetailResponse { Ok(Json(BuildDetailResponse {
build_request_id: protobuf_response.build_request_id, build_request_id: protobuf_response.build_request_id,
status_code: protobuf_response.status_code, status: protobuf_response.status,
status_name: protobuf_response.status_name,
requested_partitions: protobuf_response.requested_partitions, requested_partitions: protobuf_response.requested_partitions,
total_jobs: protobuf_response.total_jobs, total_jobs: protobuf_response.total_jobs,
completed_jobs: protobuf_response.completed_jobs, completed_jobs: protobuf_response.completed_jobs,

View file

@ -13,6 +13,7 @@ use serde::{Deserialize, Serialize};
use schemars::JsonSchema; use schemars::JsonSchema;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use rusqlite::ToSql;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use uuid::Uuid; use uuid::Uuid;
@ -398,15 +399,17 @@ impl BuildGraphService {
} }
pub fn status_to_string(status: BuildRequestStatus) -> String { pub fn status_to_string(status: BuildRequestStatus) -> String {
match status { match BuildRequestStatusCode::try_from(status.code) {
BuildRequestStatus::BuildRequestUnknown => "unknown".to_string(), Ok(BuildRequestStatusCode::BuildRequestUnknown) => "unknown".to_string(),
BuildRequestStatus::BuildRequestReceived => "received".to_string(), Ok(BuildRequestStatusCode::BuildRequestReceived) => "received".to_string(),
BuildRequestStatus::BuildRequestPlanning => "planning".to_string(), Ok(BuildRequestStatusCode::BuildRequestPlanning) => "planning".to_string(),
BuildRequestStatus::BuildRequestAnalysisCompleted => "analysis_completed".to_string(), Ok(BuildRequestStatusCode::BuildRequestAnalysisCompleted) => "analysis_completed".to_string(),
BuildRequestStatus::BuildRequestExecuting => "executing".to_string(), Ok(BuildRequestStatusCode::BuildRequestExecuting) => "executing".to_string(),
BuildRequestStatus::BuildRequestCompleted => "completed".to_string(), Ok(BuildRequestStatusCode::BuildRequestCompleted) => "completed".to_string(),
BuildRequestStatus::BuildRequestFailed => "failed".to_string(), Ok(BuildRequestStatusCode::BuildRequestFailed) => "failed".to_string(),
BuildRequestStatus::BuildRequestCancelled => "cancelled".to_string(), Ok(BuildRequestStatusCode::BuildRequestCancelled) => "cancelled".to_string(),
Ok(BuildRequestStatusCode::BuildRequestPreconditionFailed) => "precondition_failed".to_string(),
Err(_) => "error".to_string(),
} }
} }

View file

@ -61,35 +61,44 @@ impl JobStatus {
} }
} }
impl BuildRequestStatus { impl BuildRequestStatusCode {
/// Convert build request status to human-readable string matching current CLI/service format /// Convert build request status to human-readable string matching current CLI/service format
pub fn to_display_string(&self) -> String { pub fn to_display_string(&self) -> String {
match self { match self {
BuildRequestStatus::BuildRequestUnknown => "unknown".to_string(), BuildRequestStatusCode::BuildRequestUnknown => "unknown".to_string(),
BuildRequestStatus::BuildRequestReceived => "received".to_string(), BuildRequestStatusCode::BuildRequestReceived => "received".to_string(),
BuildRequestStatus::BuildRequestPlanning => "planning".to_string(), BuildRequestStatusCode::BuildRequestPlanning => "planning".to_string(),
BuildRequestStatus::BuildRequestAnalysisCompleted => "analysis_completed".to_string(), BuildRequestStatusCode::BuildRequestAnalysisCompleted => "analysis_completed".to_string(),
BuildRequestStatus::BuildRequestExecuting => "executing".to_string(), BuildRequestStatusCode::BuildRequestExecuting => "executing".to_string(),
BuildRequestStatus::BuildRequestCompleted => "completed".to_string(), BuildRequestStatusCode::BuildRequestCompleted => "completed".to_string(),
BuildRequestStatus::BuildRequestFailed => "failed".to_string(), BuildRequestStatusCode::BuildRequestFailed => "failed".to_string(),
BuildRequestStatus::BuildRequestCancelled => "cancelled".to_string(), BuildRequestStatusCode::BuildRequestCancelled => "cancelled".to_string(),
&BuildRequestStatusCode::BuildRequestPreconditionFailed => "precondition failed".to_string(),
} }
} }
/// Parse a display string back to enum /// Parse a display string back to enum
pub fn from_display_string(s: &str) -> Option<Self> { pub fn from_display_string(s: &str) -> Option<Self> {
match s { match s {
"unknown" => Some(BuildRequestStatus::BuildRequestUnknown), "unknown" => Some(BuildRequestStatusCode::BuildRequestUnknown),
"received" => Some(BuildRequestStatus::BuildRequestReceived), "received" => Some(BuildRequestStatusCode::BuildRequestReceived),
"planning" => Some(BuildRequestStatus::BuildRequestPlanning), "planning" => Some(BuildRequestStatusCode::BuildRequestPlanning),
"analysis_completed" => Some(BuildRequestStatus::BuildRequestAnalysisCompleted), "analysis_completed" => Some(BuildRequestStatusCode::BuildRequestAnalysisCompleted),
"executing" => Some(BuildRequestStatus::BuildRequestExecuting), "executing" => Some(BuildRequestStatusCode::BuildRequestExecuting),
"completed" => Some(BuildRequestStatus::BuildRequestCompleted), "completed" => Some(BuildRequestStatusCode::BuildRequestCompleted),
"failed" => Some(BuildRequestStatus::BuildRequestFailed), "failed" => Some(BuildRequestStatusCode::BuildRequestFailed),
"cancelled" => Some(BuildRequestStatus::BuildRequestCancelled), "cancelled" => Some(BuildRequestStatusCode::BuildRequestCancelled),
"precondition failed" => Some(BuildRequestStatusCode::BuildRequestPreconditionFailed),
_ => None, _ => None,
} }
} }
pub fn status(&self) -> BuildRequestStatus {
BuildRequestStatus {
code: self.clone().into(),
name: self.to_display_string(),
}
}
} }
impl DepType { impl DepType {
@ -205,11 +214,11 @@ pub mod list_response_helpers {
completed_at: Option<i64>, completed_at: Option<i64>,
duration_ms: Option<i64>, duration_ms: Option<i64>,
cancelled: bool, cancelled: bool,
comment: Option<String>,
) -> BuildSummary { ) -> BuildSummary {
BuildSummary { BuildSummary {
build_request_id, build_request_id,
status_code: status as i32, status: Some(status),
status_name: status.to_display_string(),
requested_partitions, requested_partitions,
total_jobs: total_jobs as u32, total_jobs: total_jobs as u32,
completed_jobs: completed_jobs as u32, completed_jobs: completed_jobs as u32,
@ -220,6 +229,7 @@ pub mod list_response_helpers {
completed_at, completed_at,
duration_ms, duration_ms,
cancelled, cancelled,
comment,
} }
} }
@ -256,9 +266,8 @@ mod tests {
#[test] #[test]
fn test_build_request_status_conversions() { fn test_build_request_status_conversions() {
let status = BuildRequestStatus::BuildRequestCompleted; let status = BuildRequestStatusCode::BuildRequestCompleted.status();
assert_eq!(status.to_display_string(), "completed"); assert_eq!(status.name, "completed");
assert_eq!(BuildRequestStatus::from_display_string("completed"), Some(status));
} }
#[test] #[test]
@ -276,7 +285,7 @@ mod tests {
fn test_invalid_display_string() { fn test_invalid_display_string() {
assert_eq!(PartitionStatus::from_display_string("invalid"), None); assert_eq!(PartitionStatus::from_display_string("invalid"), None);
assert_eq!(JobStatus::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); assert_eq!(DepType::from_display_string("invalid"), None);
} }
} }