Partitions page
This commit is contained in:
parent
0f51edb4b8
commit
a167339dac
4 changed files with 514 additions and 35 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import m from 'mithril';
|
||||
import { DashboardService, pollingManager, formatTime, formatDateTime, RecentActivitySummary } from './services';
|
||||
import { encodePartitionRef, BuildStatusBadge, PartitionStatusBadge, EventTypeBadge } from './utils';
|
||||
import { encodePartitionRef, decodePartitionRef, BuildStatusBadge, PartitionStatusBadge, EventTypeBadge } from './utils';
|
||||
|
||||
// Page scaffold components
|
||||
export const RecentActivity = {
|
||||
|
|
@ -500,41 +500,476 @@ export const BuildStatus = {
|
|||
};
|
||||
|
||||
export const PartitionsList = {
|
||||
view: () => m('div.container.mx-auto.p-4', [
|
||||
m('h1.text-3xl.font-bold.mb-4', 'Partitions'),
|
||||
m('div.card.bg-base-100.shadow-xl', [
|
||||
m('div.card-body', [
|
||||
m('h2.card-title', 'Partition Listing'),
|
||||
m('p', 'Searchable list of recently built partitions with build triggers.'),
|
||||
data: null as any | null,
|
||||
loading: true,
|
||||
error: null as string | null,
|
||||
searchTerm: '',
|
||||
|
||||
async loadPartitions() {
|
||||
try {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
m.redraw();
|
||||
|
||||
const { DefaultApi, Configuration } = await import('../client/typescript_generated/src/index');
|
||||
const apiClient = new DefaultApi(new Configuration({ basePath: '' }));
|
||||
|
||||
const response = await apiClient.apiV1PartitionsGet();
|
||||
this.data = response;
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
} catch (error) {
|
||||
console.error('Failed to load partitions:', error);
|
||||
this.error = error instanceof Error ? error.message : 'Failed to load partitions';
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
}
|
||||
},
|
||||
|
||||
async buildPartition(partitionRef: string) {
|
||||
try {
|
||||
const { DefaultApi, Configuration } = await import('../client/typescript_generated/src/index');
|
||||
const apiClient = new DefaultApi(new Configuration({ basePath: '' }));
|
||||
|
||||
const buildRequest = {
|
||||
partitions: [partitionRef]
|
||||
};
|
||||
|
||||
const response = await apiClient.apiV1BuildsPost({ buildRequest });
|
||||
|
||||
// Redirect to build status page
|
||||
m.route.set(`/builds/${response.buildRequestId}`);
|
||||
} catch (error) {
|
||||
console.error('Failed to start build:', error);
|
||||
alert(`Failed to start build: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
},
|
||||
|
||||
filteredPartitions() {
|
||||
if (!this.data?.partitions) return [];
|
||||
|
||||
if (!this.searchTerm) return this.data.partitions;
|
||||
|
||||
const search = this.searchTerm.toLowerCase();
|
||||
return this.data.partitions.filter((partition: any) =>
|
||||
partition.partitionRef.toLowerCase().includes(search)
|
||||
);
|
||||
},
|
||||
|
||||
oninit() {
|
||||
this.loadPartitions();
|
||||
},
|
||||
|
||||
view() {
|
||||
if (this.loading && !this.data) {
|
||||
return m('div.container.mx-auto.p-4', [
|
||||
m('div.flex.flex-col.justify-center.items-center.min-h-96', [
|
||||
m('span.loading.loading-spinner.loading-lg'),
|
||||
m('span.ml-4.text-lg.mb-4', 'Loading partitions...'),
|
||||
m('button.btn.btn-sm.btn-outline', {
|
||||
onclick: () => this.loadPartitions()
|
||||
}, 'Retry Load')
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
if (this.error) {
|
||||
return m('div.container.mx-auto.p-4', [
|
||||
m('div.alert.alert-error', [
|
||||
m('svg.stroke-current.shrink-0.h-6.w-6', {
|
||||
fill: 'none',
|
||||
viewBox: '0 0 24 24'
|
||||
}, [
|
||||
m('path', {
|
||||
'stroke-linecap': 'round',
|
||||
'stroke-linejoin': 'round',
|
||||
'stroke-width': '2',
|
||||
d: 'M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z'
|
||||
})
|
||||
]),
|
||||
m('span', this.error),
|
||||
m('div', [
|
||||
m('button.btn.btn-sm.btn-outline', {
|
||||
onclick: () => this.loadPartitions()
|
||||
}, 'Retry')
|
||||
])
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
const filteredPartitions = this.filteredPartitions();
|
||||
|
||||
return m('div.container.mx-auto.p-4', [
|
||||
m('.partitions-header.mb-6', [
|
||||
m('div.flex.justify-between.items-center.mb-4', [
|
||||
m('h1.text-3xl.font-bold', 'Partitions'),
|
||||
m('.badge.badge-primary.badge-lg', `${this.data?.totalCount || 0} total`)
|
||||
]),
|
||||
|
||||
m('div.form-control.mb-4', [
|
||||
m('input.input.input-bordered[placeholder="Search partitions..."]'),
|
||||
]),
|
||||
m('div.alert.alert-info', [
|
||||
m('span', 'Partition list will be populated from the service API.'),
|
||||
]),
|
||||
m('input.input.input-bordered.w-full.max-w-md', {
|
||||
placeholder: 'Search partitions...',
|
||||
value: this.searchTerm,
|
||||
oninput: (e: Event) => {
|
||||
this.searchTerm = (e.target as HTMLInputElement).value;
|
||||
m.redraw();
|
||||
}
|
||||
})
|
||||
])
|
||||
]),
|
||||
]),
|
||||
])
|
||||
|
||||
m('.partitions-content', [
|
||||
filteredPartitions.length === 0 ?
|
||||
m('div.card.bg-base-100.shadow-xl', [
|
||||
m('div.card-body.text-center', [
|
||||
m('h2.card-title.justify-center', 'No Partitions Found'),
|
||||
m('p.text-base-content.opacity-60',
|
||||
this.searchTerm ?
|
||||
'No partitions match your search criteria.' :
|
||||
'No partitions have been built yet.')
|
||||
])
|
||||
]) :
|
||||
m('div.card.bg-base-100.shadow-xl', [
|
||||
m('div.card-body', [
|
||||
m('h2.card-title.mb-4', `Showing ${filteredPartitions.length} partitions`),
|
||||
m('div.overflow-x-auto', [
|
||||
m('table.table.table-sm', [
|
||||
m('thead', [
|
||||
m('tr', [
|
||||
m('th', 'Partition Reference'),
|
||||
m('th', 'Status'),
|
||||
m('th', 'Last Updated'),
|
||||
m('th', 'Actions'),
|
||||
])
|
||||
]),
|
||||
m('tbody',
|
||||
filteredPartitions.map((partition: any) =>
|
||||
m('tr.hover', [
|
||||
m('td', [
|
||||
m('a.link.link-primary.font-mono.text-sm.break-all', {
|
||||
href: `/partitions/${encodePartitionRef(partition.partitionRef)}`,
|
||||
onclick: (e: Event) => {
|
||||
e.preventDefault();
|
||||
m.route.set(`/partitions/${encodePartitionRef(partition.partitionRef)}`);
|
||||
},
|
||||
title: partition.partitionRef
|
||||
}, partition.partitionRef)
|
||||
]),
|
||||
m('td', [
|
||||
m(PartitionStatusBadge, { status: partition.status })
|
||||
]),
|
||||
m('td.text-sm.opacity-70', formatTime(new Date(partition.updatedAt / 1000000).toISOString())),
|
||||
m('td', [
|
||||
m('button.btn.btn-sm.btn-primary', {
|
||||
onclick: () => this.buildPartition(partition.partitionRef)
|
||||
}, 'Build'),
|
||||
partition.buildRequestId ?
|
||||
m('a.btn.btn-sm.btn-outline.ml-2', {
|
||||
href: `/builds/${partition.buildRequestId}`,
|
||||
onclick: (e: Event) => {
|
||||
e.preventDefault();
|
||||
m.route.set(`/builds/${partition.buildRequestId}`);
|
||||
}
|
||||
}, 'View Build') : null
|
||||
])
|
||||
])
|
||||
)
|
||||
)
|
||||
])
|
||||
])
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
export const PartitionStatus = {
|
||||
view: (vnode: any) => {
|
||||
const encodedRef = vnode.attrs.base64_ref;
|
||||
data: null as any | null,
|
||||
events: null as any | null,
|
||||
loading: true,
|
||||
error: null as string | null,
|
||||
partitionRef: '',
|
||||
buildHistory: [] as any[],
|
||||
|
||||
async loadPartition() {
|
||||
try {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
m.redraw();
|
||||
|
||||
const { DefaultApi, Configuration } = await import('../client/typescript_generated/src/index');
|
||||
const apiClient = new DefaultApi(new Configuration({ basePath: '' }));
|
||||
|
||||
// Load partition status
|
||||
const statusResponse = await apiClient.apiV1PartitionsRefStatusGet({
|
||||
ref: this.partitionRef
|
||||
});
|
||||
this.data = statusResponse;
|
||||
|
||||
// Load partition events for build history
|
||||
const eventsResponse = await apiClient.apiV1PartitionsRefEventsGet({
|
||||
ref: this.partitionRef
|
||||
});
|
||||
this.events = eventsResponse;
|
||||
|
||||
// Create build history from events
|
||||
this.buildHistory = this.extractBuildHistory(eventsResponse.events);
|
||||
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
} catch (error) {
|
||||
console.error('Failed to load partition:', error);
|
||||
this.error = error instanceof Error ? error.message : 'Failed to load partition';
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
}
|
||||
},
|
||||
|
||||
extractBuildHistory(events: any[]): any[] {
|
||||
// Group events by build request ID to create build history entries
|
||||
const buildRequests = new Map();
|
||||
|
||||
events.forEach(event => {
|
||||
if (event.buildRequestId) {
|
||||
if (!buildRequests.has(event.buildRequestId)) {
|
||||
buildRequests.set(event.buildRequestId, {
|
||||
id: event.buildRequestId,
|
||||
status: 'Unknown',
|
||||
startedAt: event.timestamp,
|
||||
completedAt: null,
|
||||
events: []
|
||||
});
|
||||
}
|
||||
|
||||
const build = buildRequests.get(event.buildRequestId);
|
||||
build.events.push(event);
|
||||
|
||||
// Update status based on event type
|
||||
if (event.eventType === 'build_request') {
|
||||
if (event.message?.includes('completed') || event.message?.includes('successful')) {
|
||||
build.status = 'Completed';
|
||||
build.completedAt = event.timestamp;
|
||||
} else if (event.message?.includes('failed') || event.message?.includes('error')) {
|
||||
build.status = 'Failed';
|
||||
build.completedAt = event.timestamp;
|
||||
} else if (event.message?.includes('executing') || event.message?.includes('running')) {
|
||||
build.status = 'Executing';
|
||||
} else if (event.message?.includes('planning')) {
|
||||
build.status = 'Planning';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Convert to array and sort by start time (newest first)
|
||||
return Array.from(buildRequests.values()).sort((a, b) => b.startedAt - a.startedAt);
|
||||
},
|
||||
|
||||
async buildPartition(forceRebuild: boolean = false) {
|
||||
try {
|
||||
const { DefaultApi, Configuration } = await import('../client/typescript_generated/src/index');
|
||||
const apiClient = new DefaultApi(new Configuration({ basePath: '' }));
|
||||
|
||||
const buildRequest = {
|
||||
partitions: [this.partitionRef]
|
||||
};
|
||||
|
||||
const response = await apiClient.apiV1BuildsPost({ buildRequest });
|
||||
|
||||
// Redirect to build status page
|
||||
m.route.set(`/builds/${response.buildRequestId}`);
|
||||
} catch (error) {
|
||||
console.error('Failed to start build:', error);
|
||||
alert(`Failed to start build: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
},
|
||||
|
||||
oninit(vnode: any) {
|
||||
this.partitionRef = decodePartitionRef(vnode.attrs.base64_ref);
|
||||
this.loadPartition();
|
||||
},
|
||||
|
||||
view() {
|
||||
if (this.loading && !this.data) {
|
||||
return m('div.container.mx-auto.p-4', [
|
||||
m('div.flex.flex-col.justify-center.items-center.min-h-96', [
|
||||
m('span.loading.loading-spinner.loading-lg'),
|
||||
m('span.ml-4.text-lg.mb-4', 'Loading partition...'),
|
||||
m('button.btn.btn-sm.btn-outline', {
|
||||
onclick: () => this.loadPartition()
|
||||
}, 'Retry Load')
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
if (this.error) {
|
||||
return m('div.container.mx-auto.p-4', [
|
||||
m('div.alert.alert-error', [
|
||||
m('svg.stroke-current.shrink-0.h-6.w-6', {
|
||||
fill: 'none',
|
||||
viewBox: '0 0 24 24'
|
||||
}, [
|
||||
m('path', {
|
||||
'stroke-linecap': 'round',
|
||||
'stroke-linejoin': 'round',
|
||||
'stroke-width': '2',
|
||||
d: 'M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z'
|
||||
})
|
||||
]),
|
||||
m('span', this.error),
|
||||
m('div', [
|
||||
m('button.btn.btn-sm.btn-outline', {
|
||||
onclick: () => this.loadPartition()
|
||||
}, 'Retry')
|
||||
])
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
if (!this.data) return m('div');
|
||||
|
||||
return m('div.container.mx-auto.p-4', [
|
||||
m('h1.text-3xl.font-bold.mb-4', 'Partition Status'),
|
||||
m('div.card.bg-base-100.shadow-xl', [
|
||||
m('div.card-body', [
|
||||
m('h2.card-title', 'Partition Details'),
|
||||
m('p', 'Partition lifecycle, build history, and related information.'),
|
||||
m('div.alert.alert-info', [
|
||||
m('span', `Encoded reference: ${encodedRef}`),
|
||||
]),
|
||||
m('div.card-actions.justify-end', [
|
||||
m('button.btn.btn-primary', 'Build Now'),
|
||||
m('button.btn.btn-secondary', 'Force Rebuild'),
|
||||
// Partition Header
|
||||
m('.partition-header.mb-6', [
|
||||
m('div.flex.justify-between.items-start.mb-4', [
|
||||
m('div.flex-1', [
|
||||
m('h1.text-3xl.font-bold.mb-2', 'Partition Status'),
|
||||
m('div.font-mono.text-lg.break-all.bg-base-200.p-3.rounded', this.partitionRef)
|
||||
]),
|
||||
m('div.flex.flex-col.gap-2', [
|
||||
m('button.btn.btn-primary', {
|
||||
onclick: () => this.buildPartition(false)
|
||||
}, 'Build Now'),
|
||||
m('button.btn.btn-secondary', {
|
||||
onclick: () => this.buildPartition(true)
|
||||
}, 'Force Rebuild'),
|
||||
])
|
||||
]),
|
||||
|
||||
m('div.partition-meta.flex.gap-4.items-center.mb-4', [
|
||||
m(PartitionStatusBadge, { status: this.data?.status || 'Unknown', size: 'lg' }),
|
||||
this.data?.lastUpdated ?
|
||||
m('.timestamp.text-sm.opacity-70',
|
||||
`Last updated: ${formatDateTime(new Date(this.data.lastUpdated / 1000000).toISOString())}`) : null,
|
||||
])
|
||||
]),
|
||||
|
||||
// Main Content
|
||||
m('.partition-content.space-y-6', [
|
||||
// Build History
|
||||
m('.build-history.card.bg-base-100.shadow-xl', [
|
||||
m('.card-body', [
|
||||
m('h2.card-title.text-xl.mb-4', `Build History (${this.buildHistory?.length || 0} builds)`),
|
||||
!this.buildHistory || this.buildHistory.length === 0 ?
|
||||
m('.text-center.py-8.text-base-content.opacity-60', 'No build history available') :
|
||||
m('.overflow-x-auto', [
|
||||
m('table.table.table-sm', [
|
||||
m('thead', [
|
||||
m('tr', [
|
||||
m('th', 'Build Request'),
|
||||
m('th', 'Status'),
|
||||
m('th', 'Started'),
|
||||
m('th', 'Completed'),
|
||||
m('th', 'Events'),
|
||||
])
|
||||
]),
|
||||
m('tbody',
|
||||
this.buildHistory.map((build: any) =>
|
||||
m('tr.hover', [
|
||||
m('td', [
|
||||
m('a.link.link-primary.font-mono.text-sm', {
|
||||
href: `/builds/${build.id}`,
|
||||
onclick: (e: Event) => {
|
||||
e.preventDefault();
|
||||
m.route.set(`/builds/${build.id}`);
|
||||
}
|
||||
}, build.id)
|
||||
]),
|
||||
m('td', [
|
||||
m(BuildStatusBadge, { status: build.status })
|
||||
]),
|
||||
m('td.text-sm.opacity-70',
|
||||
formatDateTime(new Date(build.startedAt / 1000000).toISOString())),
|
||||
m('td.text-sm.opacity-70',
|
||||
build.completedAt ?
|
||||
formatDateTime(new Date(build.completedAt / 1000000).toISOString()) :
|
||||
'—'),
|
||||
m('td.text-sm.opacity-70', `${build.events?.length || 0} events`)
|
||||
])
|
||||
)
|
||||
)
|
||||
])
|
||||
])
|
||||
])
|
||||
]),
|
||||
|
||||
// Related Build Requests
|
||||
this.data?.buildRequests && this.data.buildRequests.length > 0 ?
|
||||
m('.related-builds.card.bg-base-100.shadow-xl', [
|
||||
m('.card-body', [
|
||||
m('h2.card-title.text-xl.mb-4', 'Related Build Requests'),
|
||||
m('.grid.grid-cols-1.md:grid-cols-2.lg:grid-cols-3.gap-3',
|
||||
this.data.buildRequests.map((buildId: string) =>
|
||||
m('.build-card.border.border-base-300.rounded.p-3', [
|
||||
m('a.link.link-primary.font-mono.text-sm', {
|
||||
href: `/builds/${buildId}`,
|
||||
onclick: (e: Event) => {
|
||||
e.preventDefault();
|
||||
m.route.set(`/builds/${buildId}`);
|
||||
}
|
||||
}, buildId)
|
||||
])
|
||||
)
|
||||
)
|
||||
])
|
||||
]) : null,
|
||||
|
||||
// Raw Events
|
||||
this.events?.events && this.events.events.length > 0 ?
|
||||
m('.partition-events.card.bg-base-100.shadow-xl', [
|
||||
m('.card-body', [
|
||||
m('h2.card-title.text-xl.mb-4', `All Events (${this.events.events.length})`),
|
||||
m('.overflow-x-auto', [
|
||||
m('table.table.table-xs', [
|
||||
m('thead', [
|
||||
m('tr', [
|
||||
m('th', 'Timestamp'),
|
||||
m('th', 'Event Type'),
|
||||
m('th', 'Build Request'),
|
||||
m('th', 'Message'),
|
||||
])
|
||||
]),
|
||||
m('tbody',
|
||||
this.events.events.slice(0, 20).map((event: any) => // Show first 20 events
|
||||
m('tr.hover', [
|
||||
m('td.text-xs.font-mono',
|
||||
formatDateTime(new Date(event.timestamp / 1000000).toISOString())),
|
||||
m('td', [
|
||||
m(EventTypeBadge, { eventType: event.eventType, size: 'xs' })
|
||||
]),
|
||||
m('td',
|
||||
event.buildRequestId ?
|
||||
m('a.link.link-primary.font-mono.text-xs', {
|
||||
href: `/builds/${event.buildRequestId}`,
|
||||
onclick: (e: Event) => {
|
||||
e.preventDefault();
|
||||
m.route.set(`/builds/${event.buildRequestId}`);
|
||||
}
|
||||
}, event.buildRequestId) : '—'),
|
||||
m('td.text-xs', event.message || ''),
|
||||
])
|
||||
)
|
||||
)
|
||||
])
|
||||
]),
|
||||
this.events.events.length > 20 ?
|
||||
m('.text-center.mt-4', [
|
||||
m('.text-sm.opacity-60', `Showing first 20 of ${this.events.events.length} events`)
|
||||
]) : null
|
||||
])
|
||||
]) : null
|
||||
])
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -290,15 +290,56 @@ impl BuildEventLog for SqliteBuildEventLog {
|
|||
|
||||
async fn get_partition_events(
|
||||
&self,
|
||||
_partition_ref: &str,
|
||||
_since: Option<i64>
|
||||
partition_ref: &str,
|
||||
since: Option<i64>
|
||||
) -> Result<Vec<BuildEvent>> {
|
||||
// This method is not implemented because it would require complex joins
|
||||
// to reconstruct complete event data. Use get_build_request_events instead
|
||||
// which properly reconstructs all event types for a build request.
|
||||
Err(BuildEventLogError::QueryError(
|
||||
"get_partition_events is not implemented - use get_build_request_events to get complete event data".to_string()
|
||||
))
|
||||
// First get the build request IDs (release the connection lock quickly)
|
||||
let build_ids: Vec<String> = {
|
||||
let conn = self.connection.lock().unwrap();
|
||||
|
||||
// Get all events for builds that included this partition
|
||||
// First find all build request IDs that have events for this partition
|
||||
let build_ids_query = if since.is_some() {
|
||||
"SELECT DISTINCT be.build_request_id
|
||||
FROM build_events be
|
||||
JOIN partition_events pe ON be.event_id = pe.event_id
|
||||
WHERE pe.partition_ref = ? AND be.timestamp > ?"
|
||||
} else {
|
||||
"SELECT DISTINCT be.build_request_id
|
||||
FROM build_events be
|
||||
JOIN partition_events pe ON be.event_id = pe.event_id
|
||||
WHERE pe.partition_ref = ?"
|
||||
};
|
||||
|
||||
let mut stmt = conn.prepare(build_ids_query)
|
||||
.map_err(|e| BuildEventLogError::QueryError(e.to_string()))?;
|
||||
|
||||
let row_mapper = |row: &Row| -> rusqlite::Result<String> {
|
||||
Ok(row.get::<_, String>(0)?)
|
||||
};
|
||||
|
||||
let build_ids_result: Vec<String> = if let Some(since_timestamp) = since {
|
||||
stmt.query_map(params![partition_ref, since_timestamp], row_mapper)
|
||||
} else {
|
||||
stmt.query_map(params![partition_ref], row_mapper)
|
||||
}.map_err(|e| BuildEventLogError::QueryError(e.to_string()))?
|
||||
.collect::<std::result::Result<Vec<_>, _>>()
|
||||
.map_err(|e| BuildEventLogError::QueryError(e.to_string()))?;
|
||||
|
||||
build_ids_result
|
||||
}; // Connection lock is released here
|
||||
|
||||
// Now get all events for those build requests (this gives us complete event reconstruction)
|
||||
let mut all_events = Vec::new();
|
||||
for build_id in build_ids {
|
||||
let events = self.get_build_request_events(&build_id, since).await?;
|
||||
all_events.extend(events);
|
||||
}
|
||||
|
||||
// Sort events by timestamp
|
||||
all_events.sort_by_key(|e| e.timestamp);
|
||||
|
||||
Ok(all_events)
|
||||
}
|
||||
|
||||
async fn get_job_run_events(
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ pub async fn get_build_status(
|
|||
timestamp: e.timestamp,
|
||||
event_type: event_type_to_string(&e.event_type),
|
||||
message: event_to_message(&e.event_type),
|
||||
build_request_id: e.build_request_id,
|
||||
job_label,
|
||||
partition_ref,
|
||||
delegated_build_id,
|
||||
|
|
@ -324,6 +325,7 @@ pub async fn get_partition_events(
|
|||
timestamp: e.timestamp,
|
||||
event_type: event_type_to_string(&e.event_type),
|
||||
message: event_to_message(&e.event_type),
|
||||
build_request_id: e.build_request_id,
|
||||
job_label,
|
||||
partition_ref,
|
||||
delegated_build_id,
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ pub struct BuildEventSummary {
|
|||
pub timestamp: i64,
|
||||
pub event_type: String,
|
||||
pub message: String,
|
||||
pub build_request_id: String, // Build request ID for navigation
|
||||
// Navigation-relevant fields (populated based on event type)
|
||||
pub job_label: Option<String>, // For job events
|
||||
pub partition_ref: Option<String>, // For partition events
|
||||
|
|
|
|||
Loading…
Reference in a new issue