WIP 2
This commit is contained in:
parent
232e0583f2
commit
9e8928b11a
4 changed files with 161 additions and 17 deletions
65
databuild/client/BUILD.bazel
Normal file
65
databuild/client/BUILD.bazel
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Extract OpenAPI spec from the service binary
|
||||
genrule(
|
||||
name = "extract_openapi_spec",
|
||||
srcs = [],
|
||||
outs = ["openapi.json"],
|
||||
cmd = """
|
||||
$(location //databuild:build_graph_service) --print-openapi-spec > $@
|
||||
""",
|
||||
tools = [
|
||||
"//databuild:build_graph_service",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
# TypeScript generator configuration
|
||||
filegroup(
|
||||
name = "typescript_generator_config",
|
||||
srcs = ["typescript_generator_config.json"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
# Generate TypeScript client using OpenAPI Generator JAR
|
||||
genrule(
|
||||
name = "typescript_client",
|
||||
srcs = [
|
||||
":extract_openapi_spec",
|
||||
":typescript_generator_config",
|
||||
],
|
||||
outs = [
|
||||
"typescript_generated/apis/DefaultApi.ts",
|
||||
"typescript_generated/models/index.ts",
|
||||
"typescript_generated/runtime.ts",
|
||||
"typescript_generated/index.ts",
|
||||
],
|
||||
cmd = """
|
||||
# Download OpenAPI Generator JAR
|
||||
OPENAPI_JAR=/tmp/openapi-generator-cli.jar
|
||||
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
|
||||
|
||||
# Generate TypeScript client
|
||||
java -jar $$OPENAPI_JAR generate \
|
||||
-i $(location :extract_openapi_spec) \
|
||||
-g typescript-fetch \
|
||||
-c $(location :typescript_generator_config) \
|
||||
-o $$(dirname $(location typescript_generated/index.ts))
|
||||
|
||||
# Ensure all expected output files exist
|
||||
touch $(location typescript_generated/apis/DefaultApi.ts)
|
||||
touch $(location typescript_generated/models/index.ts)
|
||||
touch $(location typescript_generated/runtime.ts)
|
||||
touch $(location typescript_generated/index.ts)
|
||||
""",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
# Main TypeScript client target
|
||||
filegroup(
|
||||
name = "typescript",
|
||||
srcs = [
|
||||
":typescript_client",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
|
@ -6,6 +6,8 @@ use axum::{
|
|||
};
|
||||
use axum_jsonschema::Json;
|
||||
use log::{error, info};
|
||||
use serde::Deserialize;
|
||||
use schemars::JsonSchema;
|
||||
use std::process::Command;
|
||||
use std::env;
|
||||
|
||||
|
|
@ -73,14 +75,19 @@ pub async fn submit_build_request(
|
|||
Ok(Json(BuildRequestResponse { build_request_id }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize, JsonSchema)]
|
||||
pub struct BuildStatusRequest {
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
pub async fn get_build_status(
|
||||
State(service): State<ServiceState>,
|
||||
Path(build_request_id): Path<String>,
|
||||
Path(request): Path<BuildStatusRequest>,
|
||||
) -> Result<Json<BuildStatusResponse>, (StatusCode, Json<ErrorResponse>)> {
|
||||
// Get build request state
|
||||
let build_state = {
|
||||
let active_builds = service.active_builds.read().await;
|
||||
active_builds.get(&build_request_id).cloned()
|
||||
active_builds.get(&request.id).cloned()
|
||||
};
|
||||
|
||||
let build_state = match build_state {
|
||||
|
|
@ -96,7 +103,7 @@ pub async fn get_build_status(
|
|||
};
|
||||
|
||||
// Get events for this build request
|
||||
let events = match service.event_log.get_build_request_events(&build_request_id, None).await {
|
||||
let events = match service.event_log.get_build_request_events(&request.id, None).await {
|
||||
Ok(events) => events.into_iter().map(|e| BuildEventSummary {
|
||||
event_id: e.event_id,
|
||||
timestamp: e.timestamp,
|
||||
|
|
@ -110,7 +117,7 @@ pub async fn get_build_status(
|
|||
};
|
||||
|
||||
Ok(Json(BuildStatusResponse {
|
||||
build_request_id,
|
||||
build_request_id: request.id,
|
||||
status: BuildGraphService::status_to_string(build_state.status),
|
||||
requested_partitions: build_state.requested_partitions,
|
||||
created_at: build_state.created_at,
|
||||
|
|
@ -119,14 +126,19 @@ pub async fn get_build_status(
|
|||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize, JsonSchema)]
|
||||
pub struct CancelBuildRequest {
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
pub async fn cancel_build_request(
|
||||
State(service): State<ServiceState>,
|
||||
Path(build_request_id): Path<String>,
|
||||
Path(request): Path<CancelBuildRequest>,
|
||||
) -> Result<Json<serde_json::Value>, (StatusCode, Json<ErrorResponse>)> {
|
||||
// Update build request state
|
||||
{
|
||||
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(&request.id) {
|
||||
build_state.status = BuildRequestStatus::BuildRequestCancelled;
|
||||
build_state.updated_at = current_timestamp_nanos();
|
||||
} else {
|
||||
|
|
@ -141,7 +153,7 @@ pub async fn cancel_build_request(
|
|||
|
||||
// Log cancellation event
|
||||
let event = create_build_event(
|
||||
build_request_id.clone(),
|
||||
request.id.clone(),
|
||||
crate::build_event::EventType::BuildRequestEvent(BuildRequestEvent {
|
||||
status: BuildRequestStatus::BuildRequestCancelled as i32,
|
||||
requested_partitions: vec![],
|
||||
|
|
@ -153,20 +165,26 @@ pub async fn cancel_build_request(
|
|||
error!("Failed to log build request cancelled event: {}", e);
|
||||
}
|
||||
|
||||
info!("Build request {} cancelled", build_request_id);
|
||||
info!("Build request {} cancelled", request.id);
|
||||
|
||||
Ok(Json(serde_json::json!({
|
||||
"cancelled": true,
|
||||
"build_request_id": build_request_id
|
||||
"build_request_id": request.id
|
||||
})))
|
||||
}
|
||||
|
||||
#[derive(Deserialize, JsonSchema)]
|
||||
pub struct PartitionStatusRequest {
|
||||
#[serde(rename = "ref")]
|
||||
pub ref_param: String,
|
||||
}
|
||||
|
||||
pub async fn get_partition_status(
|
||||
State(service): State<ServiceState>,
|
||||
Path(partition_ref): Path<String>,
|
||||
Path(partition_ref): Path<PartitionStatusRequest>,
|
||||
) -> Result<Json<PartitionStatusResponse>, (StatusCode, Json<ErrorResponse>)> {
|
||||
// Get latest partition status
|
||||
let (status, last_updated) = match service.event_log.get_latest_partition_status(&partition_ref).await {
|
||||
let (status, last_updated) = match service.event_log.get_latest_partition_status(&partition_ref.ref_param).await {
|
||||
Ok(Some((status, timestamp))) => (status, Some(timestamp)),
|
||||
Ok(None) => (PartitionStatus::PartitionUnknown, None),
|
||||
Err(e) => {
|
||||
|
|
@ -181,7 +199,7 @@ pub async fn get_partition_status(
|
|||
};
|
||||
|
||||
// Get active builds for this partition
|
||||
let build_requests = match service.event_log.get_active_builds_for_partition(&partition_ref).await {
|
||||
let build_requests = match service.event_log.get_active_builds_for_partition(&partition_ref.ref_param).await {
|
||||
Ok(builds) => builds,
|
||||
Err(e) => {
|
||||
error!("Failed to get active builds for partition: {}", e);
|
||||
|
|
@ -190,18 +208,24 @@ pub async fn get_partition_status(
|
|||
};
|
||||
|
||||
Ok(Json(PartitionStatusResponse {
|
||||
partition_ref,
|
||||
partition_ref: partition_ref.ref_param,
|
||||
status: BuildGraphService::partition_status_to_string(status),
|
||||
last_updated,
|
||||
build_requests,
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize, JsonSchema)]
|
||||
pub struct PartitionEventsRequest {
|
||||
#[serde(rename = "ref")]
|
||||
pub ref_param: String,
|
||||
}
|
||||
|
||||
pub async fn get_partition_events(
|
||||
State(service): State<ServiceState>,
|
||||
Path(partition_ref): Path<String>,
|
||||
Path(request): Path<PartitionEventsRequest>,
|
||||
) -> Result<Json<PartitionEventsResponse>, (StatusCode, Json<ErrorResponse>)> {
|
||||
let events = match service.event_log.get_partition_events(&partition_ref, None).await {
|
||||
let events = match service.event_log.get_partition_events(&request.ref_param, None).await {
|
||||
Ok(events) => events.into_iter().map(|e| BuildEventSummary {
|
||||
event_id: e.event_id,
|
||||
timestamp: e.timestamp,
|
||||
|
|
@ -220,7 +244,7 @@ pub async fn get_partition_events(
|
|||
};
|
||||
|
||||
Ok(Json(PartitionEventsResponse {
|
||||
partition_ref,
|
||||
partition_ref: request.ref_param,
|
||||
events,
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@ async fn main() {
|
|||
.help("Job lookup binary path")
|
||||
.default_value("job_lookup")
|
||||
)
|
||||
.arg(
|
||||
Arg::new("print-openapi-spec")
|
||||
.long("print-openapi-spec")
|
||||
.help("Print OpenAPI spec to stdout and exit")
|
||||
.action(clap::ArgAction::SetTrue)
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let port: u16 = matches.get_one::<String>("port").unwrap()
|
||||
|
|
@ -63,6 +69,39 @@ async fn main() {
|
|||
.map(|s| serde_json::from_str(&s).unwrap_or_else(|_| HashMap::new()))
|
||||
.unwrap_or_else(|_| HashMap::new());
|
||||
|
||||
// Handle OpenAPI spec generation
|
||||
if matches.get_flag("print-openapi-spec") {
|
||||
// Disable logging for OpenAPI generation to keep output clean
|
||||
log::set_max_level(log::LevelFilter::Off);
|
||||
|
||||
// Create a minimal service instance for OpenAPI generation
|
||||
let service = match BuildGraphService::new(
|
||||
"sqlite://:memory:", // Use in-memory database for spec generation
|
||||
graph_label,
|
||||
job_lookup_path,
|
||||
candidate_jobs,
|
||||
).await {
|
||||
Ok(service) => service,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to create service for OpenAPI generation: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Generate and print OpenAPI spec
|
||||
let spec = service.generate_openapi_spec();
|
||||
match serde_json::to_string_pretty(&spec) {
|
||||
Ok(json) => {
|
||||
println!("{}", json);
|
||||
std::process::exit(0);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to serialize OpenAPI spec: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!("Starting Build Graph Service on {}:{}", host, port);
|
||||
info!("Event log URI: {}", event_log_uri);
|
||||
info!("Graph label: {}", graph_label);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::*;
|
|||
use crate::event_log::{BuildEventLog, BuildEventLogError, create_build_event_log};
|
||||
use aide::{
|
||||
axum::{
|
||||
routing::{get, post, delete},
|
||||
routing::{get, get_with, post, delete},
|
||||
ApiRouter,
|
||||
},
|
||||
openapi::OpenApi,
|
||||
|
|
@ -113,6 +113,22 @@ impl BuildGraphService {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn generate_openapi_spec(&self) -> OpenApi {
|
||||
let mut api = OpenApi::default();
|
||||
|
||||
// Create API router with all routes to generate OpenAPI spec
|
||||
let _ = ApiRouter::new()
|
||||
.api_route("/api/v1/builds", post(handlers::submit_build_request))
|
||||
.api_route("/api/v1/builds/{id}", get(handlers::get_build_status))
|
||||
.api_route("/api/v1/builds/{id}", delete(handlers::cancel_build_request))
|
||||
.api_route("/api/v1/partitions/{ref}/status", get(handlers::get_partition_status))
|
||||
.api_route("/api/v1/partitions/{ref}/events", get(handlers::get_partition_events))
|
||||
.api_route("/api/v1/analyze", post(handlers::analyze_build_graph))
|
||||
.finish_api(&mut api);
|
||||
|
||||
api
|
||||
}
|
||||
|
||||
pub fn create_router(self) -> axum::Router {
|
||||
let mut api = OpenApi::default();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue