blob: 41317b9fba363b0053729463fcf2f1c64d778182 [file] [log] [blame]
//Contains functions for interacting with the Pinpoint backend API.
export interface Job {
job_id: string;
job_name: string;
job_type: string;
benchmark: string;
created_date: string;
job_status: string;
bot_name: string;
user: string;
}
export interface ListJobsOptions {
searchTerm?: string;
benchmark?: string;
botName?: string;
user?: string;
limit?: number;
offset?: number;
startDate?: string;
endDate?: string;
}
export interface CASDigest {
hash: string;
size_bytes: number;
}
export interface CASReference {
cas_instance: string;
digest: CASDigest;
}
// This interface represents a single commit's details.
// It mirrors the pinpointpb.Commit struct.
export interface Commit {
git_hash: string;
repository: string;
url?: string;
author?: string;
created?: string; // time.Time Golang string. General scheme is YYYY-MM-DDTHH:MM:SS.SSSSSSSSSZ
subject?: string;
message?: string;
commit_branch?: string;
commit_position?: number;
review_url?: string;
change_id?: string;
}
// This interface represents a commit with potential dependencies or patches.
// It mirrors the pinpointpb.CombinedCommit struct.
export interface CombinedCommit {
main: Commit | null;
modified_deps?: Commit[];
patch?: any | null; // This corresponds to *cabe.v1.GerritChange
}
export interface Build {
// Fields from embedded workflows.BuildParams
WorkflowID: string;
Commit: CombinedCommit | null; // This corresponds to *common.CombinedCommit
Device: string;
Target: string;
Patch: any[]; // This corresponds to []*buildbucketpb.GerritChange
Project: string;
// Fields from workflows.Build
ID: number;
Status: number; // This corresponds to buildbucketpb.Status
CAS: CASReference | null;
}
export interface TestRun {
TaskID: string;
Status: string; // This corresponds to run_benchmark.State
CAS: CASReference | null;
Architecture: string;
OSName: string;
Values: { [key: string]: number[] };
Units: { [key: string]: string };
}
export interface CommitRunData {
Build: Build | null;
Runs: TestRun[];
}
export interface CommitRuns {
left: CommitRunData;
right: CommitRunData;
}
export interface AdditionalRequestParameters {
start_commit_githash?: string;
end_commit_githash?: string;
story?: string;
story_tags?: string;
initial_attempt_count?: string;
aggregation_method?: string;
target?: string;
project?: string;
bug_id?: string;
chart?: string;
duration?: string;
commit_runs?: CommitRuns;
}
export interface WilcoxonResult {
p_value: number;
confidence_interval_lower: number;
confidence_interval_higher: number;
control_median: number;
treatment_median: number;
significant: boolean;
}
/**
* JobSchema mirrors the jobstore.JobSchema struct from the Go backend.
* The frontend will receive this entire object and can decide what to use.
*/
export interface JobSchema {
JobID: string;
JobName: string;
JobStatus: string;
JobType: string;
SubmittedBy: string;
Benchmark: string;
BotName: string;
AdditionalRequestParameters: AdditionalRequestParameters;
MetricSummary: { [chart: string]: WilcoxonResult };
ErrorMessage: string;
CreatedDate: string; // This will be a time.Time string from Go.
}
/**
* BuildStatus provides a mapping from the integer status received from the
* backend (buildbucketpb.Status) to a human-readable string.
*/
export const BuildStatus: { [key: number]: string } = {
0: 'STATUS_UNSPECIFIED',
1: 'SCHEDULED',
2: 'STARTED',
4: 'ENDED_MASK',
12: 'SUCCESS',
20: 'FAILURE',
36: 'INFRA_FAILURE',
68: 'CANCELED',
};
export interface SchedulePairwiseRequest {
start_commit?: CombinedCommit;
end_commit?: CombinedCommit;
configuration?: string;
benchmark?: string;
story?: string;
measurement?: string;
aggregation_method?: string;
target?: string;
project?: string;
bug_id?: string;
initial_attempt_count?: string;
improvement_dir?: string;
start_build?: CASReference;
end_build?: CASReference;
story_tags?: string;
user_email?: string;
job_name?: string;
}
export interface PairwiseExecution {
jobId: string;
}
export interface CancelJobRequest {
job_id: string;
reason: string;
}
export interface CancelJobResponse {
job_id: string;
state: string;
}
/**
* Schedules a new Pairwise job.
* @param request - The parameters for the pairwise job.
* @returns A promise that resolves to the execution details of the new job.
*/
export async function schedulePairwise(
request: SchedulePairwiseRequest
): Promise<PairwiseExecution> {
const response = await fetch('/pinpoint/v1/pairwise', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(request),
});
if (!response.ok) {
const errorText = await response.text();
let finalMessage = errorText;
try {
const errorJson = JSON.parse(errorText);
if (errorJson && errorJson.message) {
finalMessage = errorJson.message;
}
} catch (_) {
// Return intial error text
}
throw new Error(finalMessage);
}
return response.json();
}
/**
* Cancels a running job.
* @param request - The job ID and reason for cancellation.
* @returns A promise that resolves to the cancellation response.
*/
export async function cancelJob(request: CancelJobRequest): Promise<CancelJobResponse> {
const params = new URLSearchParams();
params.set('job_id', request.job_id);
params.set('reason', request.reason);
const response = await fetch(`/pinpoint/v1/cancel?${params.toString()}`);
if (!response.ok) {
throw new Error(`Failed to cancel job: ${response.statusText}`);
}
return response.json();
}
/**
* Fetches a list of jobs from the backend.
* @param options - The query parameters for the request.
* @returns A promise that resolves to an array of jobs.
*/
export async function listJobs(options: ListJobsOptions): Promise<Job[]> {
const params = new URLSearchParams();
if (options.searchTerm) params.set('search_term', options.searchTerm);
if (options.benchmark) params.set('benchmark', options.benchmark);
if (options.botName) params.set('bot_name', options.botName);
if (options.user) params.set('user', options.user);
if (options.limit) params.set('limit', options.limit.toString());
if (options.offset) params.set('offset', options.offset.toString());
if (options.startDate) params.set('start_date', options.startDate);
if (options.endDate) params.set('end_date', options.endDate);
const response = await fetch(`/json/jobs/list?${params.toString()}`);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to list jobs: ${errorText}: ${response.statusText}`);
}
return response.json();
}
/**
* Fetches the details for a single job.
* @param jobId - The ID of the job to fetch.
* @returns A promise that resolves to the job details.
*/
export async function getJob(jobId: string): Promise<JobSchema> {
const response = await fetch(`/json/job/${jobId}`);
if (!response.ok) {
throw new Error(`Failed to get job ${jobId}: ${response.statusText}`);
}
return response.json();
}
/**
* Fetches a list of benchmarks to run jobs against.
* @returns A promise that resolves to an array of benchmarks.
*/
export async function listBenchmarks(): Promise<string[]> {
const response = await fetch(`/benchmarks`);
if (!response.ok) {
throw new Error(`Failed to list benchmarks: ${response.statusText}`);
}
return response.json();
}
/**
* Fetches a list of bots to run jobs on based on a chosen benchmark.
* If given an empty benchmark, the function will return all bots.
* @returns A promise that resolves to an array of strings.
*/
export async function listBots(benchmark: string): Promise<string[]> {
const params = new URLSearchParams();
params.set('benchmark', benchmark);
const response = await fetch(`/bots?${params.toString()}`);
if (!response.ok) {
throw new Error(`Failed to list bots: ${response.statusText}`);
}
return response.json();
}
/**
* Fetches a list of stories to run jobs on based on a chosen benchmark.
* @returns A promise that resolves to an array of strings.
*/
export async function listStories(benchmark: string): Promise<string[]> {
const params = new URLSearchParams();
if (benchmark === '') {
throw new Error(`Failed to list stories: No benchmark provided`);
}
params.set('benchmark', benchmark);
const response = await fetch(`/stories?${params.toString()}`);
if (!response.ok) {
throw new Error(`Failed to list stories: ${response.statusText}`);
}
return response.json();
}