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
52 changes: 9 additions & 43 deletions report-app/report-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@ import {
isMainModule,
writeResponseToNodeResponse,
} from '@angular/ssr/node';
import { glob } from 'tinyglobby';
import express from 'express';
import { readFile } from 'node:fs/promises';
import { dirname, isAbsolute, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import {
FetchedLocalReports,
fetchReportsFromDisk,
} from '../runner/reporting/report-local-disk';

const app = express();
const reportsLoaderPromise = getReportLoader();
const options = getOptions();
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const angularApp = new AngularNodeAppEngine();
let localDataPromise: Promise<LocalData> | null = null;
let localDataPromise: Promise<FetchedLocalReports> | null = null;

// Endpoint for fetching all available report groups.
app.get('/api/reports', async (_, res) => {
Expand All @@ -26,8 +28,8 @@ app.get('/api/reports', async (_, res) => {
]);
const results = remoteGroups.slice();

for (const [, group] of localData) {
results.unshift(group.overview);
for (const [, data] of localData) {
results.unshift(data.group);
}

res.json(results);
Expand Down Expand Up @@ -77,14 +79,6 @@ interface ReportLoader {
getGroupsList: () => Promise<{ id: string }[]>;
}

type LocalData = Map<
string,
{
overview: { id: string };
run: { group: string };
}
>;

/** Gets the server options from the command line. */
function getOptions() {
const defaultPort = 4200;
Expand Down Expand Up @@ -135,37 +129,9 @@ async function getReportLoader() {
async function resolveLocalData(directory: string) {
// Reuse the same promise so that concurrent requests get the same response.
if (!localDataPromise) {
let resolveFn: (data: LocalData) => void;
let resolveFn: (data: FetchedLocalReports) => void;
localDataPromise = new Promise((resolve) => (resolveFn = resolve));

const data: LocalData = new Map();
const groupFiles = await glob('**/groups.json', {
cwd: directory,
absolute: true,
});

await Promise.all(
// Note: sort the groups so that the indexes stay consistent no matter how the files
// appear on disk. It appears to be non-deterministic when using the async glob.
groupFiles.sort().map(async (configPath, index) => {
const [groupContent, runContent] = await Promise.all([
readFile(configPath, 'utf8'),
readFile(join(dirname(configPath), 'summary.json'), 'utf8'),
]);

// Note: Local reports only have one group.
const overview = (JSON.parse(groupContent) as { id: string }[])[0];
const run = JSON.parse(runContent) as { group: string };

// Local runs should not be grouped by their group ID, but rather if they
// were part of the same invocation. Add a unique suffix to the ID to
// prevent further grouping.
run.group = overview.id = `${overview.id}-l${index}`;
data.set(overview.id, { overview, run });
})
);

resolveFn!(data);
resolveFn!(await fetchReportsFromDisk(directory));
}

return localDataPromise;
Expand Down
1 change: 1 addition & 0 deletions runner/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ export { getRunnerByName, type RunnerName } from './codegen/runner-creation.js';
export { getEnvironmentByPath } from './configuration/environment-resolution.js';
export { type Environment } from './configuration/environment.js';
export { autoRateFiles } from './ratings/autoraters/rate-files.js';
export { fetchReportsFromDisk } from './reporting/report-local-disk.js';
47 changes: 47 additions & 0 deletions runner/reporting/report-local-disk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { readFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { RunGroup, RunInfo } from '../shared-interfaces.js';
import { glob } from 'tinyglobby';

/** Type describing a map from group report IDs to their runs. */
export type FetchedLocalReports = Map<
/* groupId */ string,
{
group: RunGroup;
run: RunInfo;
}
>;

/** Fetches local report data from the given directory. */
export async function fetchReportsFromDisk(
directory: string
): Promise<FetchedLocalReports> {
const data: FetchedLocalReports = new Map();
const groupFiles = await glob('**/groups.json', {
cwd: directory,
absolute: true,
});

await Promise.all(
// Note: sort the groups so that the indexes stay consistent no matter how the files
// appear on disk. It appears to be non-deterministic when using the async glob.
groupFiles.sort().map(async (configPath, index) => {
const [groupContent, runContent] = await Promise.all([
readFile(configPath, 'utf8'),
readFile(join(dirname(configPath), 'summary.json'), 'utf8'),
]);

// Note: Local reports only have one group.
const group = (JSON.parse(groupContent) as RunGroup[])[0];
const run = JSON.parse(runContent) as RunInfo;

// Local runs should not be grouped by their group ID, but rather if they
// were part of the same invocation. Add a unique suffix to the ID to
// prevent further grouping.
run.group = group.id = `${group.id}-l${index}`;
data.set(group.id, { group, run });
})
);

return data;
}
Loading