api camelCase -> snake_case

This commit is contained in:
Stuart Axelbrooke 2025-07-16 21:30:32 -07:00
parent b70a6ce9a7
commit 45433da0b8
6 changed files with 91 additions and 90 deletions

View file

@ -1,4 +1,4 @@
load("@aspect_rules_ts//ts:defs.bzl", "ts_project", "ts_config") load("@aspect_rules_ts//ts:defs.bzl", "ts_config", "ts_project")
# Extract OpenAPI spec from the dedicated spec generator binary # Extract OpenAPI spec from the dedicated spec generator binary
genrule( genrule(
@ -64,17 +64,17 @@ genrule(
if [ ! -f $$OPENAPI_JAR ]; then if [ ! -f $$OPENAPI_JAR ]; then
curl -L -o $$OPENAPI_JAR https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.2.0/openapi-generator-cli-7.2.0.jar curl -L -o $$OPENAPI_JAR https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.2.0/openapi-generator-cli-7.2.0.jar
fi fi
# Create temporary directory for generation # Create temporary directory for generation
TEMP_DIR=$$(mktemp -d) TEMP_DIR=$$(mktemp -d)
# Generate TypeScript client to temp directory # Generate TypeScript client to temp directory
java -jar $$OPENAPI_JAR generate \ java -jar $$OPENAPI_JAR generate \
-i $(location :extract_openapi_spec) \ -i $(location :extract_openapi_spec) \
-g typescript-fetch \ -g typescript-fetch \
-c $(location :typescript_generator_config) \ -c $(location :typescript_generator_config) \
-o $$TEMP_DIR -o $$TEMP_DIR
# Copy generated files to expected output locations # Copy generated files to expected output locations
cp $$TEMP_DIR/src/apis/DefaultApi.ts $(location typescript_generated/src/apis/DefaultApi.ts) cp $$TEMP_DIR/src/apis/DefaultApi.ts $(location typescript_generated/src/apis/DefaultApi.ts)
cp $$TEMP_DIR/src/apis/index.ts $(location typescript_generated/src/apis/index.ts) cp $$TEMP_DIR/src/apis/index.ts $(location typescript_generated/src/apis/index.ts)
@ -134,4 +134,4 @@ filegroup(
":typescript_client", ":typescript_client",
], ],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
) )

View file

@ -1,5 +1,5 @@
{ {
"enumPropertyNaming": "camelCase", "enumPropertyNaming": "snake_case",
"withInterfaces": true, "withInterfaces": true,
"useSingleRequestParameter": true, "useSingleRequestParameter": true,
"typescriptThreePlus": true, "typescriptThreePlus": true,
@ -7,8 +7,8 @@
"npmVersion": "1.0.0", "npmVersion": "1.0.0",
"stringEnums": true, "stringEnums": true,
"generateAliasAsModel": false, "generateAliasAsModel": false,
"modelPropertyNaming": "camelCase", "modelPropertyNaming": "snake_case",
"paramNaming": "camelCase", "paramNaming": "snake_case",
"supportsES6": true, "supportsES6": true,
"withoutRuntimeChecks": false "withoutRuntimeChecks": false
} }

View file

@ -194,12 +194,12 @@ export const RecentActivity = {
m('tr.hover', [ m('tr.hover', [
m('td', [ m('td', [
m('a.link.link-primary.font-mono.text-sm', { m('a.link.link-primary.font-mono.text-sm', {
href: `/builds/${build.id}`, href: `/builds/${build.buildRequestId}`,
onclick: (e: Event) => { onclick: (e: Event) => {
e.preventDefault(); e.preventDefault();
m.route.set(`/builds/${build.id}`); m.route.set(`/builds/${build.buildRequestId}`);
} }
}, build.id) }, build.buildRequestId)
]), ]),
m('td', [ m('td', [
m(BuildStatusBadge, { status: build.status }) m(BuildStatusBadge, { status: build.status })
@ -299,19 +299,19 @@ export const BuildStatus = {
const apiClient = new DefaultApi(new Configuration({ basePath: '' })); const apiClient = new DefaultApi(new Configuration({ basePath: '' }));
// Get build status // Get build status
const buildResponse = await apiClient.apiV1BuildsBuildRequestIdGet({ buildRequestId: this.buildId }); const buildResponse = await apiClient.apiV1BuildsBuildRequestIdGet({ build_request_id: this.buildId });
this.data = buildResponse; this.data = buildResponse;
// Load partition statuses for all requested partitions // Load partition statuses for all requested partitions
if (buildResponse.requestedPartitions) { if (buildResponse.requested_partitions) {
for (const partitionRef of buildResponse.requestedPartitions) { for (const partition_ref of buildResponse.requested_partitions) {
try { try {
const partitionStatus = await apiClient.apiV1PartitionsRefStatusGet({ const partition_status = await apiClient.apiV1PartitionsRefStatusGet({
ref: partitionRef ref: partition_ref
}); });
this.partitionStatuses.set(partitionRef, partitionStatus); this.partitionStatuses.set(partition_ref, partition_status);
} catch (e) { } catch (e) {
console.warn(`Failed to load status for partition ${partitionRef}:`, e); console.warn(`Failed to load status for partition ${partition_ref}:`, e);
} }
} }
} }
@ -339,29 +339,28 @@ export const BuildStatus = {
getEventLink(event: any): { href: string; text: string } | null { getEventLink(event: any): { href: string; text: string } | null {
const eventType = event.eventType; console.warn("event", event, event.event_type);
switch (event.event_type) {
switch (eventType) {
case 'job': case 'job':
if (event.jobLabel) { if (event.job_label) {
return { return {
href: `/jobs/${encodeURIComponent(event.jobLabel)}`, href: `/jobs/${encodeURIComponent(event.job_label)}`,
text: 'Job Details' text: 'Job Details'
}; };
} }
return null; return null;
case 'partition': case 'partition':
if (event.partitionRef) { if (event.partition_ref) {
return { return {
href: `/partitions/${encodePartitionRef(event.partitionRef)}`, href: `/partitions/${encodePartitionRef(event.partition_ref)}`,
text: 'Partition Status' text: 'Partition Status'
}; };
} }
return null; return null;
case 'delegation': case 'delegation':
if (event.delegatedBuildId) { if (event.delegated_build_id) {
return { return {
href: `/builds/${event.delegatedBuildId}`, href: `/builds/${event.delegated_build_id}`,
text: 'Delegated Build' text: 'Delegated Build'
}; };
} }
@ -419,7 +418,7 @@ export const BuildStatus = {
m('h1.text-3xl.font-bold.mb-4', `Build ${this.buildId}`), m('h1.text-3xl.font-bold.mb-4', `Build ${this.buildId}`),
m('.build-meta.flex.gap-4.items-center.mb-4', [ m('.build-meta.flex.gap-4.items-center.mb-4', [
m(BuildStatusBadge, { status: this.data.status, size: 'lg' }), m(BuildStatusBadge, { status: this.data.status, size: 'lg' }),
m('.timestamp.text-sm.opacity-70', formatDateTime(new Date(this.data.createdAt).toISOString())), m('.timestamp.text-sm.opacity-70', formatDateTime(this.data.created_at)),
m('.partitions.text-sm.opacity-70', `${this.data.requestedPartitions?.length || 0} partitions`), m('.partitions.text-sm.opacity-70', `${this.data.requestedPartitions?.length || 0} partitions`),
]) ])
]), ]),
@ -444,7 +443,7 @@ export const BuildStatus = {
m(PartitionStatusBadge, { status: status?.status || 'Unknown' }), m(PartitionStatusBadge, { status: status?.status || 'Unknown' }),
status?.lastUpdated ? status?.lastUpdated ?
m('.updated-time.text-xs.opacity-60', m('.updated-time.text-xs.opacity-60',
formatDateTime(new Date(status.lastUpdated).toISOString())) : null formatDateTime(status.last_updated)) : null
]) ])
]); ]);
}) || [m('.text-center.py-8.text-base-content.opacity-60', 'No partitions')] }) || [m('.text-center.py-8.text-base-content.opacity-60', 'No partitions')]
@ -470,14 +469,15 @@ export const BuildStatus = {
this.data.events.map((event: any) => this.data.events.map((event: any) =>
m('tr.hover', [ m('tr.hover', [
m('td.text-xs.font-mono', m('td.text-xs.font-mono',
formatDateTime(new Date(event.timestamp).toISOString())), formatDateTime(event.timestamp)),
m('td', [ m('td', [
m(EventTypeBadge, { eventType: event.eventType }) m(EventTypeBadge, { eventType: event.event_type })
]), ]),
m('td.text-sm', event.message || ''), m('td.text-sm', event.message || ''),
m('td', [ m('td', [
(() => { (() => {
const link = this.getEventLink(event); const link = this.getEventLink(event);
console.warn("link", link);
return link ? return link ?
m(m.route.Link, { m(m.route.Link, {
href: link.href, href: link.href,
@ -531,14 +531,14 @@ export const PartitionsList = {
const { DefaultApi, Configuration } = await import('../client/typescript_generated/src/index'); const { DefaultApi, Configuration } = await import('../client/typescript_generated/src/index');
const apiClient = new DefaultApi(new Configuration({ basePath: '' })); const apiClient = new DefaultApi(new Configuration({ basePath: '' }));
const buildRequest = { const build_request = {
partitions: [partitionRef] partitions: [partitionRef]
}; };
const response = await apiClient.apiV1BuildsPost({ buildRequest }); const response = await apiClient.apiV1BuildsPost({ build_request });
// Redirect to build status page // Redirect to build status page
m.route.set(`/builds/${response.buildRequestId}`); m.route.set(`/builds/${response.build_request_id}`);
} catch (error) { } catch (error) {
console.error('Failed to start build:', error); console.error('Failed to start build:', error);
alert(`Failed to start build: ${error instanceof Error ? error.message : 'Unknown error'}`); alert(`Failed to start build: ${error instanceof Error ? error.message : 'Unknown error'}`);
@ -658,7 +658,7 @@ export const PartitionsList = {
m('td', [ m('td', [
m(PartitionStatusBadge, { status: partition.status }) m(PartitionStatusBadge, { status: partition.status })
]), ]),
m('td.text-sm.opacity-70', formatTime(new Date(partition.updated_at).toISOString())), m('td.text-sm.opacity-70', formatTime(partition.updated_at)),
m('td', [ m('td', [
m('button.btn.btn-sm.btn-primary', { m('button.btn.btn-sm.btn-primary', {
onclick: () => this.buildPartition(partition.partition_ref) onclick: () => this.buildPartition(partition.partition_ref)
@ -771,14 +771,14 @@ export const PartitionStatus = {
const { DefaultApi, Configuration } = await import('../client/typescript_generated/src/index'); const { DefaultApi, Configuration } = await import('../client/typescript_generated/src/index');
const apiClient = new DefaultApi(new Configuration({ basePath: '' })); const apiClient = new DefaultApi(new Configuration({ basePath: '' }));
const buildRequest = { const build_request = {
partitions: [this.partitionRef] partitions: [this.partitionRef]
}; };
const response = await apiClient.apiV1BuildsPost({ buildRequest }); const response = await apiClient.apiV1BuildsPost({ build_request });
// Redirect to build status page // Redirect to build status page
m.route.set(`/builds/${response.buildRequestId}`); m.route.set(`/builds/${response.build_request_id}`);
} catch (error) { } catch (error) {
console.error('Failed to start build:', error); console.error('Failed to start build:', error);
alert(`Failed to start build: ${error instanceof Error ? error.message : 'Unknown error'}`); alert(`Failed to start build: ${error instanceof Error ? error.message : 'Unknown error'}`);
@ -851,7 +851,7 @@ export const PartitionStatus = {
m(PartitionStatusBadge, { status: this.data?.status || 'Unknown', size: 'lg' }), m(PartitionStatusBadge, { status: this.data?.status || 'Unknown', size: 'lg' }),
this.data?.lastUpdated ? this.data?.lastUpdated ?
m('.timestamp.text-sm.opacity-70', m('.timestamp.text-sm.opacity-70',
`Last updated: ${formatDateTime(new Date(this.data.lastUpdated).toISOString())}`) : null, `Last updated: ${formatDateTime(this.data.lastUpdated)}`) : null,
]) ])
]), ]),
@ -890,10 +890,10 @@ export const PartitionStatus = {
m(BuildStatusBadge, { status: build.status }) m(BuildStatusBadge, { status: build.status })
]), ]),
m('td.text-sm.opacity-70', m('td.text-sm.opacity-70',
formatDateTime(new Date(build.startedAt).toISOString())), formatDateTime(build.startedAt)),
m('td.text-sm.opacity-70', m('td.text-sm.opacity-70',
build.completedAt ? build.completedAt ?
formatDateTime(new Date(build.completedAt).toISOString()) : formatDateTime(build.completedAt) :
'—'), '—'),
m('td.text-sm.opacity-70', `${build.events?.length || 0} events`) m('td.text-sm.opacity-70', `${build.events?.length || 0} events`)
]) ])
@ -941,31 +941,31 @@ export const PartitionStatus = {
]) ])
]), ]),
m('tbody', m('tbody',
this.events.events.slice(0, 20).map((event: any) => // Show first 20 events this.events.events.slice(0, 100).map((event: any) => // Show first 100 events
m('tr.hover', [ m('tr.hover', [
m('td.text-xs.font-mono', m('td.text-xs.font-mono',
formatDateTime(new Date(event.timestamp).toISOString())), formatDateTime(event.timestamp)),
m('td', [ m('td', [
m(EventTypeBadge, { eventType: event.eventType, size: 'xs' }) m(EventTypeBadge, { eventType: event.event_type, size: 'xs' })
]), ]),
m('td', m('td',
event.buildRequestId ? event.build_request_id ?
m('a.link.link-primary.font-mono.text-xs', { m('a.link.link-primary.font-mono.text-xs', {
href: `/builds/${event.buildRequestId}`, href: `/builds/${event.build_request_id}`,
onclick: (e: Event) => { onclick: (e: Event) => {
e.preventDefault(); e.preventDefault();
m.route.set(`/builds/${event.buildRequestId}`); m.route.set(`/builds/${event.build_request_id}`);
} }
}, event.buildRequestId) : '—'), }, event.build_request_id) : '—'),
m('td.text-xs', event.message || ''), m('td.text-xs', event.message || ''),
]) ])
) )
) )
]) ])
]), ]),
this.events.events.length > 20 ? this.events.events.length > 100 ?
m('.text-center.mt-4', [ m('.text-center.mt-4', [
m('.text-sm.opacity-60', `Showing first 20 of ${this.events.events.length} events`) m('.text-sm.opacity-60', `Showing first 100 of ${this.events.events.length} events`)
]) : null ]) : null
]) ])
]) : null ]) : null
@ -1087,7 +1087,7 @@ export const JobsList = {
m('td', formatDuration(job.avg_duration_ms)), m('td', formatDuration(job.avg_duration_ms)),
m('td', (job.recent_runs || 0).toString()), m('td', (job.recent_runs || 0).toString()),
m('td.text-sm.opacity-70', m('td.text-sm.opacity-70',
job.last_run ? formatTime(new Date(job.last_run).toISOString()) : '—'), job.last_run ? formatTime(job.last_run) : '—'),
]) ])
)) ))
]) ])
@ -1254,7 +1254,7 @@ export const JobMetrics = {
]), ]),
m('td', formatDuration(run.duration_ms)), m('td', formatDuration(run.duration_ms)),
m('td.text-sm.opacity-70', m('td.text-sm.opacity-70',
formatTime(new Date(run.started_at).toISOString())), formatTime(run.started_at)),
]) ])
)) ))
]) ])

View file

@ -1,9 +1,6 @@
// Import the generated TypeScript client // Import the generated TypeScript client
import { DefaultApi, Configuration, ActivityResponse, BuildSummary, PartitionSummary, JobsListResponse, JobMetricsResponse, JobSummary, JobRunSummary, JobDailyStats } from '../client/typescript_generated/src/index'; import { DefaultApi, Configuration, ActivityResponse, BuildSummary, PartitionSummary, JobsListResponse, JobMetricsResponse, JobSummary, JobRunSummary, JobDailyStats } from '../client/typescript_generated/src/index';
// Base API configuration
const API_BASE = '/api/v1';
// Configure the API client // Configure the API client
const apiConfig = new Configuration({ const apiConfig = new Configuration({
basePath: '', // Use relative paths since we're on the same host basePath: '', // Use relative paths since we're on the same host
@ -12,16 +9,16 @@ const apiClient = new DefaultApi(apiConfig);
// Types for dashboard data - using the generated API types // Types for dashboard data - using the generated API types
export interface BuildRequest { export interface BuildRequest {
id: string; buildRequestId: string;
status: string; status: string;
createdAt: string; createdAt: number;
updatedAt: string; updatedAt: number;
} }
export interface PartitionBuild { export interface PartitionBuild {
ref: string; ref: string;
status: string; status: string;
updatedAt: string; updatedAt: number;
buildRequestId?: string; buildRequestId?: string;
} }
@ -49,29 +46,30 @@ export class DashboardService {
try { try {
// Use the new activity endpoint that aggregates all the data we need // Use the new activity endpoint that aggregates all the data we need
const activityResponse: ActivityResponse = await apiClient.apiV1ActivityGet(); const activityResponse: ActivityResponse = await apiClient.apiV1ActivityGet();
console.info('Recent activity:', activityResponse);
// Convert the API response to our dashboard format // Convert the API response to our dashboard format
const recentBuilds: BuildRequest[] = activityResponse.recentBuilds.map((build: BuildSummary) => ({ const recentBuilds: BuildRequest[] = activityResponse.recent_builds.map((build: BuildSummary) => ({
id: build.buildRequestId, buildRequestId: build.build_request_id,
status: build.status, status: build.status,
createdAt: new Date(build.createdAt).toISOString(), // Backend now sends milliseconds createdAt: build.created_at,
updatedAt: new Date(build.updatedAt).toISOString() updatedAt: build.updated_at,
})); }));
const recentPartitions: PartitionBuild[] = activityResponse.recentPartitions.map((partition: PartitionSummary) => ({ const recentPartitions: PartitionBuild[] = activityResponse.recent_partitions.map((partition: PartitionSummary) => ({
ref: partition.partitionRef, ref: partition.partition_ref,
status: partition.status, status: partition.status,
updatedAt: new Date(partition.updatedAt).toISOString(), updatedAt: partition.updated_at,
buildRequestId: partition.buildRequestId || undefined buildRequestId: partition.build_request_id || undefined
})); }));
console.info("made", recentBuilds, recentPartitions);
return { return {
activeBuilds: activityResponse.activeBuildsCount, activeBuilds: activityResponse.active_builds_count,
recentBuilds, recentBuilds,
recentPartitions, recentPartitions,
totalPartitions: activityResponse.totalPartitionsCount, totalPartitions: activityResponse.total_partitions_count,
systemStatus: activityResponse.systemStatus, systemStatus: activityResponse.system_status,
graphName: activityResponse.graphName graphName: activityResponse.graph_name
}; };
} catch (error) { } catch (error) {
console.error('Failed to fetch recent activity:', error); console.error('Failed to fetch recent activity:', error);
@ -209,8 +207,8 @@ export class PollingManager {
export const pollingManager = new PollingManager(); export const pollingManager = new PollingManager();
// Utility functions for time formatting // Utility functions for time formatting
export function formatTime(isoString: string): string { export function formatTime(epochNanos: number): string {
const date = new Date(isoString); const date = new Date(epochNanos / 1000000);
const now = new Date(); const now = new Date();
const diffMs = now.getTime() - date.getTime(); const diffMs = now.getTime() - date.getTime();
@ -227,8 +225,8 @@ export function formatTime(isoString: string): string {
} }
} }
export function formatDateTime(isoString: string): string { export function formatDateTime(epochNanos: number): string {
const date = new Date(isoString); const date = new Date(epochNanos / 1000000);
const dateStr = date.toLocaleDateString('en-US'); const dateStr = date.toLocaleDateString('en-US');
const timeStr = date.toLocaleTimeString('en-US', { const timeStr = date.toLocaleTimeString('en-US', {
hour: 'numeric', hour: 'numeric',
@ -243,7 +241,8 @@ export function formatDateTime(isoString: string): string {
return `${dateStr}, ${timeStr.replace(/(\d{2})\s+(AM|PM)/, `$1.${millisStr} $2`)}`; return `${dateStr}, ${timeStr.replace(/(\d{2})\s+(AM|PM)/, `$1.${millisStr} $2`)}`;
} }
export function formatDuration(durationMs?: number | null): string { export function formatDuration(durationNanos?: number | null): string {
let durationMs = durationNanos ? durationNanos / 1000000 : null;
if (!durationMs || durationMs <= 0) { if (!durationMs || durationMs <= 0) {
return '—'; return '—';
} }
@ -263,8 +262,8 @@ export function formatDuration(durationMs?: number | null): string {
} }
} }
export function formatDate(dateString: string): string { export function formatDate(epochNanos: number): string {
const date = new Date(dateString); const date = new Date(epochNanos / 1000000);
return date.toLocaleDateString('en-US', { return date.toLocaleDateString('en-US', {
month: 'short', month: 'short',
day: 'numeric', day: 'numeric',

View file

@ -223,7 +223,7 @@ pub async fn get_build_status(
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 / 1_000_000, // Convert nanoseconds to milliseconds 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,
@ -272,8 +272,8 @@ pub async fn get_build_status(
build_request_id, build_request_id,
status: final_status_string, status: final_status_string,
requested_partitions, requested_partitions,
created_at: created_at / 1_000_000, // Convert nanoseconds to milliseconds created_at: created_at,
updated_at: updated_at / 1_000_000, // Convert nanoseconds to milliseconds updated_at: updated_at,
events: event_summaries, events: event_summaries,
job_graph: job_graph_json, job_graph: job_graph_json,
mermaid_diagram, mermaid_diagram,
@ -397,7 +397,7 @@ pub async fn get_partition_events(
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 / 1_000_000, // Convert nanoseconds to milliseconds 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,
@ -708,8 +708,8 @@ pub async fn list_build_requests(
build_request_id: s.build_request_id, build_request_id: s.build_request_id,
status: format!("{:?}", s.status), status: format!("{:?}", s.status),
requested_partitions: s.requested_partitions, requested_partitions: s.requested_partitions,
created_at: s.created_at / 1_000_000, // Convert nanoseconds to milliseconds created_at: s.created_at,
updated_at: s.updated_at / 1_000_000, // Convert nanoseconds to milliseconds updated_at: s.updated_at,
}).collect(); }).collect();
let has_more = (offset + limit) < total_count; let has_more = (offset + limit) < total_count;
@ -761,7 +761,7 @@ pub async fn list_partitions(
let partitions: Vec<PartitionSummary> = summaries.into_iter().map(|s| PartitionSummary { let partitions: Vec<PartitionSummary> = summaries.into_iter().map(|s| PartitionSummary {
partition_ref: s.partition_ref, partition_ref: s.partition_ref,
status: format!("{:?}", s.status), status: format!("{:?}", s.status),
updated_at: s.updated_at / 1_000_000, // Convert nanoseconds to milliseconds updated_at: s.updated_at,
build_request_id: s.build_request_id, build_request_id: s.build_request_id,
}).collect(); }).collect();
@ -794,14 +794,14 @@ pub async fn get_activity_summary(
build_request_id: s.build_request_id, build_request_id: s.build_request_id,
status: format!("{:?}", s.status), status: format!("{:?}", s.status),
requested_partitions: s.requested_partitions, requested_partitions: s.requested_partitions,
created_at: s.created_at / 1_000_000, // Convert nanoseconds to milliseconds created_at: s.created_at,
updated_at: s.updated_at / 1_000_000, // Convert nanoseconds to milliseconds updated_at: s.updated_at,
}).collect(); }).collect();
let recent_partitions: Vec<PartitionSummary> = summary.recent_partitions.into_iter().map(|s| PartitionSummary { let recent_partitions: Vec<PartitionSummary> = summary.recent_partitions.into_iter().map(|s| PartitionSummary {
partition_ref: s.partition_ref, partition_ref: s.partition_ref,
status: format!("{:?}", s.status), status: format!("{:?}", s.status),
updated_at: s.updated_at / 1_000_000, // Convert nanoseconds to milliseconds updated_at: s.updated_at,
build_request_id: s.build_request_id, build_request_id: s.build_request_id,
}).collect(); }).collect();
@ -1044,7 +1044,7 @@ pub async fn get_job_metrics(
let started_at: i64 = row[3].parse().unwrap_or(0); let started_at: i64 = row[3].parse().unwrap_or(0);
let completed_at: i64 = row[4].parse().unwrap_or(started_at); let completed_at: i64 = row[4].parse().unwrap_or(started_at);
let duration_ms: Option<i64> = if completed_at > started_at { let duration_ms: Option<i64> = if completed_at > started_at {
Some((completed_at - started_at) / 1_000_000) // Convert nanoseconds to milliseconds Some((completed_at - started_at))
} else { } else {
None None
}; };

View file

@ -119,6 +119,7 @@ pub struct BuildSummary {
pub updated_at: i64, pub updated_at: i64,
} }
// TODO snake cased response
#[derive(Debug, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct PartitionsListResponse { pub struct PartitionsListResponse {
pub partitions: Vec<PartitionSummary>, pub partitions: Vec<PartitionSummary>,
@ -134,6 +135,7 @@ pub struct PartitionSummary {
pub build_request_id: Option<String>, pub build_request_id: Option<String>,
} }
// TODO camel cased results
#[derive(Debug, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct ActivityResponse { pub struct ActivityResponse {
pub active_builds_count: u32, pub active_builds_count: u32,