Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 15 additions & 19 deletions src/backend/compare/compare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,29 @@ import type {
} from '../../shared/view-types.js';
import { refreshSecret } from '../util.js';
import { deleteReport, renderCompare } from './report.js';
import type { TimelineRequest } from '../../shared/api.js';
import type { TimelineRequest, TimelineResponse } from '../../shared/api.js';
import { getNumberOrError } from '../request-check.js';
import { log } from '../logging.js';

export async function getProfileAsJson(
ctx: ParameterizedContext,
db: Database
): Promise<void> {
ctx.type = 'application/json';

): Promise<ProfileRow[]> {
const runId = getNumberOrError(ctx, 'runId');
if (runId === null) {
log.error((ctx.body as any).error);
return;
ctx.status = 404;
return null as any;
}

const start = startRequest();

ctx.body = await getProfile(runId, ctx.params.commitId, db);
if (ctx.body === undefined) {
const result = await getProfile(runId, ctx.params.commitId, db);
if (result === undefined || result.length === 0) {
ctx.status = 404;
ctx.body = {};
}
completeRequestAndHandlePromise(start, db, 'get-profiles');
return result;
}

async function getProfile(
Expand Down Expand Up @@ -73,18 +72,17 @@ async function getProfile(
export async function getMeasurementsAsJson(
ctx: ParameterizedContext,
db: Database
): Promise<void> {
ctx.type = 'application/json';

): Promise<WarmupDataForTrial[] | null> {
const runId = getNumberOrError(ctx, 'runId');
if (runId === null) {
log.error((ctx.body as any).error);
return;
ctx.status = 404;
return null;
}

const start = startRequest();

ctx.body = await getMeasurements(
const result = await getMeasurements(
ctx.params.projectSlug,
runId,
ctx.params.baseId,
Expand All @@ -93,6 +91,7 @@ export async function getMeasurementsAsJson(
);

completeRequestAndHandlePromise(start, db, 'get-measurements');
return result;
}

/**
Expand Down Expand Up @@ -171,20 +170,17 @@ export async function getMeasurements(
export async function getTimelineDataAsJson(
ctx: ParameterizedContext,
db: Database
): Promise<void> {
): Promise<TimelineResponse> {
const timelineRequest = <TimelineRequest>(<unknown>ctx.request.body);
const result = await db.getTimelineData(
ctx.params.projectName,
timelineRequest
);
if (result === null) {
ctx.body = { error: 'Requested data was not found' };
ctx.status = 404;
} else {
ctx.body = result;
ctx.status = 200;
return { error: 'Requested data was not found' } as any;
}
ctx.type = 'json';
return result;
}

export async function renderComparePage(
Expand Down
37 changes: 16 additions & 21 deletions src/backend/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import {
startRequest
} from '../perf-tracker.js';
import type { AllResults } from '../../shared/api.js';
import type { ChangesResponse } from '../../shared/view-types.js';
import type {
ChangesResponse,
SiteStatsResponse
} from '../../shared/view-types.js';
import { Database } from '../db/db.js';
import { TimedCacheValidity } from '../db/timed-cache-validity.js';
import { getNumberOrError } from '../request-check.js';
Expand All @@ -37,18 +40,18 @@ export async function renderMainPage(
export async function getLast100MeasurementsAsJson(
ctx: ParameterizedContext,
db: Database
): Promise<void> {
ctx.type = 'application/json';

): Promise<AllResults[]> {
const projectId = getNumberOrError(ctx, 'projectId');
if (projectId === null) {
log.error((ctx.body as any).error);
return;
ctx.status = 404;
return null as any;
}

const start = startRequest();
ctx.body = await getLast100Measurements(projectId, db);
const result = await getLast100Measurements(projectId, db);
completeRequestAndHandlePromise(start, db, 'get-results');
return result;
}

const resultsCache: AllResults[][] = [];
Expand Down Expand Up @@ -121,24 +124,17 @@ export async function getLast100Measurements(
return resultsCache[projectId];
}

export async function getSiteStatsAsJson(
ctx: ParameterizedContext,
db: Database
): Promise<void> {
ctx.body = await getStatistics(db);
ctx.type = 'application/json';
}

let statisticsCache: { stats: any[]; version: number } | null = null;
let statsCacheValid: TimedCacheValidity | null = null;

export function statsCache(): TimedCacheValidity | null {
return statsCacheValid;
}

export async function getStatistics(
export async function getSiteStatsAsJson(
_: ParameterizedContext,
db: Database
): Promise<{ stats: any[]; version: number }> {
): Promise<SiteStatsResponse> {
if (
statisticsCache !== null &&
statsCacheValid !== null &&
Expand Down Expand Up @@ -174,16 +170,15 @@ export async function getStatistics(
export async function getChangesAsJson(
ctx: ParameterizedContext,
db: Database
): Promise<void> {
ctx.type = 'application/json';

): Promise<ChangesResponse> {
const projectId = getNumberOrError(ctx, 'projectId');
if (projectId === null) {
log.error((ctx.body as any).error);
return;
ctx.status = 404;
return null as any;
}

ctx.body = await getChanges(projectId, db);
return await getChanges(projectId, db);
}

export async function getChanges(
Expand Down
9 changes: 4 additions & 5 deletions src/backend/project/data-export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,15 @@ export async function getExpData(
export async function getAvailableDataAsJson(
ctx: ParameterizedContext,
db: Database
): Promise<void> {
ctx.type = 'application/json';

): Promise<{ data: any[] }> {
const projectId = getNumberOrError(ctx, 'projectId');
if (projectId === null) {
log.error((ctx.body as any).error);
return;
ctx.status = 404;
return null as any;
}

ctx.body = await getDataOverview(projectId, db);
return await getDataOverview(projectId, db);
}

export async function getDataOverview(
Expand Down
18 changes: 9 additions & 9 deletions src/backend/project/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { getExpData } from './data-export.js';
import { Database } from '../db/db.js';
import { rebenchVersion, robustPath } from '../../backend/util.js';
import { Source } from '../db/types.js';

const projectHtml = prepareTemplate(robustPath('backend/project/project.html'));

Expand All @@ -30,22 +31,21 @@ export async function renderProjectPage(
export async function getSourceAsJson(
ctx: ParameterizedContext,
db: Database
): Promise<void> {
): Promise<Source | string> {
const result = await db.getSourceById(
ctx.params.projectSlug,
ctx.params.sourceId
);

if (result !== null) {
ctx.body = result;
ctx.type = 'application/json';
} else {
respondProjectAndSourceNotFound(
ctx,
ctx.params.projectSlug,
ctx.params.sourceId
);
return result;
}

return respondProjectAndSourceNotFound(
ctx,
ctx.params.projectSlug,
ctx.params.sourceId
);
}

const projectDataTpl = prepareTemplate(
Expand Down
32 changes: 32 additions & 0 deletions src/backend/server-routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Middleware, ParameterizedContext } from 'koa';
import { apiRoutes, ApiRoutes } from '../shared/routes.js';
import Router from '@koa/router';
import { Database } from './db/db.js';

export function defineRoute<Path extends keyof ApiRoutes>(
path: Path,
router: Router,
db: Database,
handler: (
ctx: ParameterizedContext,
db: Database
) => Promise<ApiRoutes[Path]['response']>,
koaBody: Middleware | null = null
): void {
switch (apiRoutes[path].method) {
case 'GET':
router.get(path, async (ctx) => {
const result = await handler(ctx, db);
ctx.type = 'application/json';
ctx.body = result;
});
break;
case 'POST':
router.post(path, koaBody!, async (ctx) => {
const result = await handler(ctx, db);
ctx.type = 'application/json';
ctx.body = result;
});
break;
}
}
9 changes: 5 additions & 4 deletions src/backend/standard-responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ export function respondProjectAndSourceNotFound(
ctx: ParameterizedContext,
projectSlug: string,
sourceId: string
): void {
ctx.body =
`Requested combination of project "${projectSlug}"` +
` and source ${sourceId} not found`;
): string {
ctx.status = 404;
ctx.type = 'text';
return (
`Requested combination of project "${projectSlug}"` +
` and source ${sourceId} not found`
);
}
17 changes: 9 additions & 8 deletions src/backend/timeline/timeline.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ParameterizedContext } from 'koa';
import { respondProjectNotFound } from '../standard-responses.js';
import { prepareTemplate } from '../templates.js';
import { TimelineSuite } from '../../shared/api.js';
import { TimelineResponse, TimelineSuite } from '../../shared/api.js';
import { Database } from '../db/db.js';
import { rebenchVersion, robustPath } from '../util.js';
import { getNumberOrError } from '../request-check.js';
Expand All @@ -15,25 +15,26 @@ const timelineTpl = prepareTemplate(
export async function getTimelineAsJson(
ctx: ParameterizedContext,
db: Database
): Promise<void> {
ctx.type = 'application/json';

): Promise<TimelineResponse | null> {
const projectId = getNumberOrError(ctx, 'projectId');
if (projectId === null) {
log.error((ctx.body as any).error);
return;
ctx.status = 404;
return null;
}

const runId = getNumberOrError(ctx, 'runId');
if (runId === null) {
log.error((ctx.body as any).error);
return;
ctx.status = 404;
return null;
}

ctx.body = await db.getTimelineForRun(projectId, runId);
if (ctx.body === null) {
const result = await db.getTimelineForRun(projectId, runId);
if (result === null) {
ctx.status = 500;
}
return result;
}

export async function renderTimeline(
Expand Down
39 changes: 39 additions & 0 deletions src/frontend/api-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ApiRoutes, apiRoutes } from '../shared/routes.js';

type ExtractParams<Path extends string> =
Path extends `${string}:${infer Param}/${infer Rest}`
? { [K in Param | keyof ExtractParams<Rest>]: string | number }
: Path extends `${string}:${infer Param}`
? { [K in Param]: string | number }
: Record<string, never>;

export async function apiFetch<Path extends keyof ApiRoutes>(
path: Path,
params: ExtractParams<Path>,
jsonData?: any
): Promise<ApiRoutes[Path]['response']> {
let url: string = path;
for (const [key, value] of Object.entries(params)) {
url = url.replace(`:${key}`, String(value));
}

let options: RequestInit | undefined = undefined;
if (apiRoutes[path].method === 'POST') {
options = {
method: 'POST',
mode: 'same-origin',
cache: 'no-cache',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
redirect: 'follow',
referrerPolicy: 'no-referrer',
body: JSON.stringify(jsonData)
};
}

const res = await fetch(url, options);
if (!res.ok) {
throw new Error(`API request failed with status ${res.status}`);
}
return res.json();
}
Loading