From 45433da0b8e45d04614cbcb91123dbc8d3231f7e Mon Sep 17 00:00:00 2001 From: Stuart Axelbrooke Date: Wed, 16 Jul 2025 21:30:32 -0700 Subject: [PATCH] api camelCase -> snake_case --- databuild/client/BUILD.bazel | 10 +-- .../client/typescript_generator_config.json | 6 +- databuild/dashboard/pages.ts | 88 +++++++++---------- databuild/dashboard/services.ts | 53 ++++++----- databuild/service/handlers.rs | 22 ++--- databuild/service/mod.rs | 2 + 6 files changed, 91 insertions(+), 90 deletions(-) diff --git a/databuild/client/BUILD.bazel b/databuild/client/BUILD.bazel index 21a671d..a883a8e 100644 --- a/databuild/client/BUILD.bazel +++ b/databuild/client/BUILD.bazel @@ -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 genrule( @@ -64,17 +64,17 @@ genrule( 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 fi - + # Create temporary directory for generation TEMP_DIR=$$(mktemp -d) - + # Generate TypeScript client to temp directory java -jar $$OPENAPI_JAR generate \ -i $(location :extract_openapi_spec) \ -g typescript-fetch \ -c $(location :typescript_generator_config) \ -o $$TEMP_DIR - + # 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/index.ts $(location typescript_generated/src/apis/index.ts) @@ -134,4 +134,4 @@ filegroup( ":typescript_client", ], visibility = ["//visibility:public"], -) \ No newline at end of file +) diff --git a/databuild/client/typescript_generator_config.json b/databuild/client/typescript_generator_config.json index ba47474..3f4d5bf 100644 --- a/databuild/client/typescript_generator_config.json +++ b/databuild/client/typescript_generator_config.json @@ -1,5 +1,5 @@ { - "enumPropertyNaming": "camelCase", + "enumPropertyNaming": "snake_case", "withInterfaces": true, "useSingleRequestParameter": true, "typescriptThreePlus": true, @@ -7,8 +7,8 @@ "npmVersion": "1.0.0", "stringEnums": true, "generateAliasAsModel": false, - "modelPropertyNaming": "camelCase", - "paramNaming": "camelCase", + "modelPropertyNaming": "snake_case", + "paramNaming": "snake_case", "supportsES6": true, "withoutRuntimeChecks": false } \ No newline at end of file diff --git a/databuild/dashboard/pages.ts b/databuild/dashboard/pages.ts index 8b74529..8e1d512 100644 --- a/databuild/dashboard/pages.ts +++ b/databuild/dashboard/pages.ts @@ -194,12 +194,12 @@ export const RecentActivity = { m('tr.hover', [ m('td', [ m('a.link.link-primary.font-mono.text-sm', { - href: `/builds/${build.id}`, + href: `/builds/${build.buildRequestId}`, onclick: (e: Event) => { e.preventDefault(); - m.route.set(`/builds/${build.id}`); + m.route.set(`/builds/${build.buildRequestId}`); } - }, build.id) + }, build.buildRequestId) ]), m('td', [ m(BuildStatusBadge, { status: build.status }) @@ -299,19 +299,19 @@ export const BuildStatus = { const apiClient = new DefaultApi(new Configuration({ basePath: '' })); // Get build status - const buildResponse = await apiClient.apiV1BuildsBuildRequestIdGet({ buildRequestId: this.buildId }); + const buildResponse = await apiClient.apiV1BuildsBuildRequestIdGet({ build_request_id: this.buildId }); this.data = buildResponse; // Load partition statuses for all requested partitions - if (buildResponse.requestedPartitions) { - for (const partitionRef of buildResponse.requestedPartitions) { + if (buildResponse.requested_partitions) { + for (const partition_ref of buildResponse.requested_partitions) { try { - const partitionStatus = await apiClient.apiV1PartitionsRefStatusGet({ - ref: partitionRef + const partition_status = await apiClient.apiV1PartitionsRefStatusGet({ + ref: partition_ref }); - this.partitionStatuses.set(partitionRef, partitionStatus); + this.partitionStatuses.set(partition_ref, partition_status); } 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 { - const eventType = event.eventType; - - switch (eventType) { + console.warn("event", event, event.event_type); + switch (event.event_type) { case 'job': - if (event.jobLabel) { + if (event.job_label) { return { - href: `/jobs/${encodeURIComponent(event.jobLabel)}`, + href: `/jobs/${encodeURIComponent(event.job_label)}`, text: 'Job Details' }; } return null; case 'partition': - if (event.partitionRef) { + if (event.partition_ref) { return { - href: `/partitions/${encodePartitionRef(event.partitionRef)}`, + href: `/partitions/${encodePartitionRef(event.partition_ref)}`, text: 'Partition Status' }; } return null; case 'delegation': - if (event.delegatedBuildId) { + if (event.delegated_build_id) { return { - href: `/builds/${event.delegatedBuildId}`, + href: `/builds/${event.delegated_build_id}`, text: 'Delegated Build' }; } @@ -419,7 +418,7 @@ export const BuildStatus = { m('h1.text-3xl.font-bold.mb-4', `Build ${this.buildId}`), m('.build-meta.flex.gap-4.items-center.mb-4', [ 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`), ]) ]), @@ -444,7 +443,7 @@ export const BuildStatus = { m(PartitionStatusBadge, { status: status?.status || 'Unknown' }), status?.lastUpdated ? 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')] @@ -470,14 +469,15 @@ export const BuildStatus = { this.data.events.map((event: any) => m('tr.hover', [ m('td.text-xs.font-mono', - formatDateTime(new Date(event.timestamp).toISOString())), + formatDateTime(event.timestamp)), m('td', [ - m(EventTypeBadge, { eventType: event.eventType }) + m(EventTypeBadge, { eventType: event.event_type }) ]), m('td.text-sm', event.message || ''), m('td', [ (() => { const link = this.getEventLink(event); + console.warn("link", link); return link ? m(m.route.Link, { href: link.href, @@ -531,14 +531,14 @@ export const PartitionsList = { const { DefaultApi, Configuration } = await import('../client/typescript_generated/src/index'); const apiClient = new DefaultApi(new Configuration({ basePath: '' })); - const buildRequest = { + const build_request = { partitions: [partitionRef] }; - const response = await apiClient.apiV1BuildsPost({ buildRequest }); + const response = await apiClient.apiV1BuildsPost({ build_request }); // Redirect to build status page - m.route.set(`/builds/${response.buildRequestId}`); + m.route.set(`/builds/${response.build_request_id}`); } catch (error) { console.error('Failed to start build:', error); alert(`Failed to start build: ${error instanceof Error ? error.message : 'Unknown error'}`); @@ -658,7 +658,7 @@ export const PartitionsList = { m('td', [ 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('button.btn.btn-sm.btn-primary', { onclick: () => this.buildPartition(partition.partition_ref) @@ -771,14 +771,14 @@ export const PartitionStatus = { const { DefaultApi, Configuration } = await import('../client/typescript_generated/src/index'); const apiClient = new DefaultApi(new Configuration({ basePath: '' })); - const buildRequest = { + const build_request = { partitions: [this.partitionRef] }; - const response = await apiClient.apiV1BuildsPost({ buildRequest }); + const response = await apiClient.apiV1BuildsPost({ build_request }); // Redirect to build status page - m.route.set(`/builds/${response.buildRequestId}`); + m.route.set(`/builds/${response.build_request_id}`); } catch (error) { console.error('Failed to start build:', 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' }), this.data?.lastUpdated ? 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('td.text-sm.opacity-70', - formatDateTime(new Date(build.startedAt).toISOString())), + formatDateTime(build.startedAt)), m('td.text-sm.opacity-70', build.completedAt ? - formatDateTime(new Date(build.completedAt).toISOString()) : + formatDateTime(build.completedAt) : '—'), m('td.text-sm.opacity-70', `${build.events?.length || 0} events`) ]) @@ -941,31 +941,31 @@ export const PartitionStatus = { ]) ]), 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('td.text-xs.font-mono', - formatDateTime(new Date(event.timestamp).toISOString())), + formatDateTime(event.timestamp)), m('td', [ - m(EventTypeBadge, { eventType: event.eventType, size: 'xs' }) + m(EventTypeBadge, { eventType: event.event_type, size: 'xs' }) ]), m('td', - event.buildRequestId ? + event.build_request_id ? m('a.link.link-primary.font-mono.text-xs', { - href: `/builds/${event.buildRequestId}`, + href: `/builds/${event.build_request_id}`, onclick: (e: Event) => { 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 || ''), ]) ) ) ]) ]), - this.events.events.length > 20 ? + this.events.events.length > 100 ? 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 @@ -1087,7 +1087,7 @@ export const JobsList = { m('td', formatDuration(job.avg_duration_ms)), m('td', (job.recent_runs || 0).toString()), 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.text-sm.opacity-70', - formatTime(new Date(run.started_at).toISOString())), + formatTime(run.started_at)), ]) )) ]) diff --git a/databuild/dashboard/services.ts b/databuild/dashboard/services.ts index 5c5564f..4feb8b9 100644 --- a/databuild/dashboard/services.ts +++ b/databuild/dashboard/services.ts @@ -1,9 +1,6 @@ // Import the generated TypeScript client 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 const apiConfig = new Configuration({ 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 export interface BuildRequest { - id: string; + buildRequestId: string; status: string; - createdAt: string; - updatedAt: string; + createdAt: number; + updatedAt: number; } export interface PartitionBuild { ref: string; status: string; - updatedAt: string; + updatedAt: number; buildRequestId?: string; } @@ -49,29 +46,30 @@ export class DashboardService { try { // Use the new activity endpoint that aggregates all the data we need const activityResponse: ActivityResponse = await apiClient.apiV1ActivityGet(); + console.info('Recent activity:', activityResponse); // Convert the API response to our dashboard format - const recentBuilds: BuildRequest[] = activityResponse.recentBuilds.map((build: BuildSummary) => ({ - id: build.buildRequestId, + const recentBuilds: BuildRequest[] = activityResponse.recent_builds.map((build: BuildSummary) => ({ + buildRequestId: build.build_request_id, status: build.status, - createdAt: new Date(build.createdAt).toISOString(), // Backend now sends milliseconds - updatedAt: new Date(build.updatedAt).toISOString() + createdAt: build.created_at, + updatedAt: build.updated_at, })); - const recentPartitions: PartitionBuild[] = activityResponse.recentPartitions.map((partition: PartitionSummary) => ({ - ref: partition.partitionRef, + const recentPartitions: PartitionBuild[] = activityResponse.recent_partitions.map((partition: PartitionSummary) => ({ + ref: partition.partition_ref, status: partition.status, - updatedAt: new Date(partition.updatedAt).toISOString(), - buildRequestId: partition.buildRequestId || undefined + updatedAt: partition.updated_at, + buildRequestId: partition.build_request_id || undefined })); - + console.info("made", recentBuilds, recentPartitions); return { - activeBuilds: activityResponse.activeBuildsCount, + activeBuilds: activityResponse.active_builds_count, recentBuilds, recentPartitions, - totalPartitions: activityResponse.totalPartitionsCount, - systemStatus: activityResponse.systemStatus, - graphName: activityResponse.graphName + totalPartitions: activityResponse.total_partitions_count, + systemStatus: activityResponse.system_status, + graphName: activityResponse.graph_name }; } catch (error) { console.error('Failed to fetch recent activity:', error); @@ -209,8 +207,8 @@ export class PollingManager { export const pollingManager = new PollingManager(); // Utility functions for time formatting -export function formatTime(isoString: string): string { - const date = new Date(isoString); +export function formatTime(epochNanos: number): string { + const date = new Date(epochNanos / 1000000); const now = new Date(); const diffMs = now.getTime() - date.getTime(); @@ -227,8 +225,8 @@ export function formatTime(isoString: string): string { } } -export function formatDateTime(isoString: string): string { - const date = new Date(isoString); +export function formatDateTime(epochNanos: number): string { + const date = new Date(epochNanos / 1000000); const dateStr = date.toLocaleDateString('en-US'); const timeStr = date.toLocaleTimeString('en-US', { hour: 'numeric', @@ -243,7 +241,8 @@ export function formatDateTime(isoString: string): string { 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) { return '—'; } @@ -263,8 +262,8 @@ export function formatDuration(durationMs?: number | null): string { } } -export function formatDate(dateString: string): string { - const date = new Date(dateString); +export function formatDate(epochNanos: number): string { + const date = new Date(epochNanos / 1000000); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', diff --git a/databuild/service/handlers.rs b/databuild/service/handlers.rs index 0578469..6c3d387 100644 --- a/databuild/service/handlers.rs +++ b/databuild/service/handlers.rs @@ -223,7 +223,7 @@ pub async fn get_build_status( let (job_label, partition_ref, delegated_build_id) = extract_navigation_data(&e.event_type); BuildEventSummary { 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), message: event_to_message(&e.event_type), build_request_id: e.build_request_id, @@ -272,8 +272,8 @@ pub async fn get_build_status( build_request_id, status: final_status_string, requested_partitions, - created_at: created_at / 1_000_000, // Convert nanoseconds to milliseconds - updated_at: updated_at / 1_000_000, // Convert nanoseconds to milliseconds + created_at: created_at, + updated_at: updated_at, events: event_summaries, job_graph: job_graph_json, 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); BuildEventSummary { 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), message: event_to_message(&e.event_type), build_request_id: e.build_request_id, @@ -708,8 +708,8 @@ pub async fn list_build_requests( build_request_id: s.build_request_id, status: format!("{:?}", s.status), requested_partitions: s.requested_partitions, - created_at: s.created_at / 1_000_000, // Convert nanoseconds to milliseconds - updated_at: s.updated_at / 1_000_000, // Convert nanoseconds to milliseconds + created_at: s.created_at, + updated_at: s.updated_at, }).collect(); let has_more = (offset + limit) < total_count; @@ -761,7 +761,7 @@ pub async fn list_partitions( let partitions: Vec = summaries.into_iter().map(|s| PartitionSummary { partition_ref: s.partition_ref, 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, }).collect(); @@ -794,14 +794,14 @@ pub async fn get_activity_summary( build_request_id: s.build_request_id, status: format!("{:?}", s.status), requested_partitions: s.requested_partitions, - created_at: s.created_at / 1_000_000, // Convert nanoseconds to milliseconds - updated_at: s.updated_at / 1_000_000, // Convert nanoseconds to milliseconds + created_at: s.created_at, + updated_at: s.updated_at, }).collect(); let recent_partitions: Vec = summary.recent_partitions.into_iter().map(|s| PartitionSummary { partition_ref: s.partition_ref, 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, }).collect(); @@ -1044,7 +1044,7 @@ pub async fn get_job_metrics( let started_at: i64 = row[3].parse().unwrap_or(0); let completed_at: i64 = row[4].parse().unwrap_or(started_at); let duration_ms: Option = if completed_at > started_at { - Some((completed_at - started_at) / 1_000_000) // Convert nanoseconds to milliseconds + Some((completed_at - started_at)) } else { None }; diff --git a/databuild/service/mod.rs b/databuild/service/mod.rs index 6954a25..6d7f5f6 100644 --- a/databuild/service/mod.rs +++ b/databuild/service/mod.rs @@ -119,6 +119,7 @@ pub struct BuildSummary { pub updated_at: i64, } +// TODO snake cased response #[derive(Debug, Serialize, Deserialize, JsonSchema)] pub struct PartitionsListResponse { pub partitions: Vec, @@ -134,6 +135,7 @@ pub struct PartitionSummary { pub build_request_id: Option, } +// TODO camel cased results #[derive(Debug, Serialize, Deserialize, JsonSchema)] pub struct ActivityResponse { pub active_builds_count: u32,