diff --git a/apps/arc-web/app/routes.ts b/apps/arc-web/app/routes.ts index 849744220..8759b626f 100644 --- a/apps/arc-web/app/routes.ts +++ b/apps/arc-web/app/routes.ts @@ -27,7 +27,7 @@ export default [ route("stages/:stageId", "routes/run-stages.tsx"), route("configuration", "routes/run-configuration.tsx"), route("graph", "routes/run-graph.tsx"), - route("compare", "routes/run-compare.tsx"), + route("files", "routes/run-files.tsx"), route("verification", "routes/run-verification.tsx"), route("usage", "routes/run-usage.tsx"), route("retro", "routes/run-retro.tsx"), diff --git a/apps/arc-web/app/routes/run-detail.tsx b/apps/arc-web/app/routes/run-detail.tsx index 9b3b9eff8..f1e27dc40 100644 --- a/apps/arc-web/app/routes/run-detail.tsx +++ b/apps/arc-web/app/routes/run-detail.tsx @@ -11,7 +11,7 @@ import type { Route } from "./+types/run-detail"; const tabs = [ { name: "Overview", path: "", count: null }, { name: "Stages", path: "/stages/detect-drift", count: null }, - { name: "Files Changed", path: "/compare", count: null }, + { name: "Files Changed", path: "/files", count: null }, { name: "Verification", path: "/verification", count: null }, { name: "Retro", path: "/retro", count: null }, { name: "Usage", path: "/usage", count: null }, diff --git a/apps/arc-web/app/routes/run-compare.tsx b/apps/arc-web/app/routes/run-files.tsx similarity index 85% rename from apps/arc-web/app/routes/run-compare.tsx rename to apps/arc-web/app/routes/run-files.tsx index 1f3590765..942cf45eb 100644 --- a/apps/arc-web/app/routes/run-compare.tsx +++ b/apps/arc-web/app/routes/run-files.tsx @@ -1,6 +1,4 @@ import { useCallback, useEffect, useRef, useState } from "react"; -import { useParams } from "react-router"; -import { ChevronDownIcon, Cog6ToothIcon } from "@heroicons/react/24/outline"; import { MultiFileDiff, type AnnotationSide, @@ -8,13 +6,13 @@ import { } from "@pierre/diffs/react"; import { useTheme } from "../lib/theme"; import { apiJson } from "../api-client"; -import type { RunCompare } from "@qltysh/arc-api-client"; -import type { Route } from "./+types/run-compare"; +import type { PaginatedRunFileList } from "@qltysh/arc-api-client"; +import type { Route } from "./+types/run-files"; export const handle = { wide: true }; export async function loader({ request, params }: Route.LoaderArgs) { - const data = await apiJson(`/runs/${params.id}/compare`, { request }); + const data = await apiJson(`/runs/${params.id}/files`, { request }); return data; } @@ -217,29 +215,6 @@ export async function execute( }, ]; -const BLOCK_COUNT = 5; - -function DiffStat({ additions, deletions }: { additions: number; deletions: number }) { - const total = additions + deletions; - const addBlocks = total === 0 ? 0 : Math.round((additions / total) * BLOCK_COUNT); - const delBlocks = BLOCK_COUNT - addBlocks; - - return ( -
- +{additions.toLocaleString()} - -{deletions.toLocaleString()} -
- {Array.from({ length: BLOCK_COUNT }, (_, i) => ( - - ))} -
-
- ); -} - interface SteerAnnotation { fileName: string; lineNumber: number; @@ -508,21 +483,14 @@ function buildAnnotationsForFile( return annotations; } -export default function RunCompare({ loaderData }: Route.ComponentProps) { +export default function RunFiles({ loaderData }: Route.ComponentProps) { const runFiles = loaderData; - const checkpoints = [ - { id: "all", label: "All changes" }, - ...runFiles.checkpoints.map((cp) => ({ id: cp.id, label: cp.label })), - ]; - const files = runFiles.files.length > 0 - ? runFiles.files.map((f) => ({ + const files = runFiles.data.length > 0 + ? runFiles.data.map((f) => ({ oldFile: { name: f.old_file.name, contents: f.old_file.contents }, newFile: { name: f.new_file.name, contents: f.new_file.contents }, })) : fallbackFiles; - const diffStats = runFiles.stats; - - const [checkpoint, setCheckpoint] = useState(checkpoints[0].id); const [openSteers, setOpenSteers] = useState( () => new Map(), ); @@ -560,31 +528,6 @@ export default function RunCompare({ loaderData }: Route.ComponentProps) { return (
-
-
- - -
-
- - -
-
- {files.map(({ oldFile, newFile }) => ( -**`GET /runs/{id}/files` renamed to `GET /runs/{id}/compare`.** The endpoint returns the same data but the path now reflects its purpose of comparing file changes between checkpoints. +**`GET /runs/{id}/compare` renamed to `GET /runs/{id}/files`.** The endpoint now returns a standard paginated list of `FileDiff` items instead of the `RunCompare` envelope. -To migrate: Update API integrations using `/files` to use `/compare`. +To migrate: Update API integrations using `/compare` to use `/files`, and adjust response parsing from `{ checkpoints, files, stats }` to `{ data, meta }`. diff --git a/packages/arc-api-client/src/.openapi-generator/FILES b/packages/arc-api-client/src/.openapi-generator/FILES index 4ef7bef53..793f5d7ac 100644 --- a/packages/arc-api-client/src/.openapi-generator/FILES +++ b/packages/arc-api-client/src/.openapi-generator/FILES @@ -74,6 +74,7 @@ models/paginated-api-question-list.ts models/paginated-history-entry-list.ts models/paginated-model-list.ts models/paginated-retro-list.ts +models/paginated-run-file-list.ts models/paginated-run-list.ts models/paginated-run-stage-list.ts models/paginated-run-verification-list.ts @@ -95,7 +96,6 @@ models/retro-stats.ts models/root-response-urls.ts models/root-response.ts models/run-checkpoint.ts -models/run-compare.ts models/run-configuration.ts models/run-error.ts models/run-list-item.ts diff --git a/packages/arc-api-client/src/api/run-outputs-api.ts b/packages/arc-api-client/src/api/run-outputs-api.ts index 90b0cf2c4..b2dd0b561 100644 --- a/packages/arc-api-client/src/api/run-outputs-api.ts +++ b/packages/arc-api-client/src/api/run-outputs-api.ts @@ -24,9 +24,9 @@ import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError // @ts-ignore import type { ErrorResponse } from '../models'; // @ts-ignore -import type { PaginatedRunVerificationList } from '../models'; +import type { PaginatedRunFileList } from '../models'; // @ts-ignore -import type { RunCompare } from '../models'; +import type { PaginatedRunVerificationList } from '../models'; // @ts-ignore import type { RunUsage } from '../models'; /** @@ -35,17 +35,19 @@ import type { RunUsage } from '../models'; export const RunOutputsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** - * Returns file-level diffs produced by the run, optionally filtered to a specific checkpoint. - * @summary Retrieve Run Compare + * Returns a paginated list of file-level diffs produced by the run, optionally filtered to a specific checkpoint. + * @summary Retrieve Run Files * @param {string} id Unique run identifier (ULID). * @param {string} [checkpoint] Filter to a specific checkpoint ID. Omit to include all changes. + * @param {number} [pageLimit] Maximum number of items to return per page. + * @param {number} [pageOffset] Number of items to skip before returning results. * @param {*} [options] Override http request option. * @throws {RequiredError} */ - retrieveRunCompare: async (id: string, checkpoint?: string, options: RawAxiosRequestConfig = {}): Promise => { + retrieveRunFiles: async (id: string, checkpoint?: string, pageLimit?: number, pageOffset?: number, options: RawAxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined - assertParamExists('retrieveRunCompare', 'id', id) - const localVarPath = `/runs/{id}/compare` + assertParamExists('retrieveRunFiles', 'id', id) + const localVarPath = `/runs/{id}/files` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -69,6 +71,14 @@ export const RunOutputsApiAxiosParamCreator = function (configuration?: Configur localVarQueryParameter['checkpoint'] = checkpoint; } + if (pageLimit !== undefined) { + localVarQueryParameter['page[limit]'] = pageLimit; + } + + if (pageOffset !== undefined) { + localVarQueryParameter['page[offset]'] = pageOffset; + } + localVarHeaderParameter['Accept'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); @@ -182,17 +192,19 @@ export const RunOutputsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = RunOutputsApiAxiosParamCreator(configuration) return { /** - * Returns file-level diffs produced by the run, optionally filtered to a specific checkpoint. - * @summary Retrieve Run Compare + * Returns a paginated list of file-level diffs produced by the run, optionally filtered to a specific checkpoint. + * @summary Retrieve Run Files * @param {string} id Unique run identifier (ULID). * @param {string} [checkpoint] Filter to a specific checkpoint ID. Omit to include all changes. + * @param {number} [pageLimit] Maximum number of items to return per page. + * @param {number} [pageOffset] Number of items to skip before returning results. * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async retrieveRunCompare(id: string, checkpoint?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.retrieveRunCompare(id, checkpoint, options); + async retrieveRunFiles(id: string, checkpoint?: string, pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.retrieveRunFiles(id, checkpoint, pageLimit, pageOffset, options); const localVarOperationServerIndex = configuration?.serverIndex ?? 0; - const localVarOperationServerBasePath = operationServerMap['RunOutputsApi.retrieveRunCompare']?.[localVarOperationServerIndex]?.url; + const localVarOperationServerBasePath = operationServerMap['RunOutputsApi.retrieveRunFiles']?.[localVarOperationServerIndex]?.url; return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); }, /** @@ -233,15 +245,17 @@ export const RunOutputsApiFactory = function (configuration?: Configuration, bas const localVarFp = RunOutputsApiFp(configuration) return { /** - * Returns file-level diffs produced by the run, optionally filtered to a specific checkpoint. - * @summary Retrieve Run Compare + * Returns a paginated list of file-level diffs produced by the run, optionally filtered to a specific checkpoint. + * @summary Retrieve Run Files * @param {string} id Unique run identifier (ULID). * @param {string} [checkpoint] Filter to a specific checkpoint ID. Omit to include all changes. + * @param {number} [pageLimit] Maximum number of items to return per page. + * @param {number} [pageOffset] Number of items to skip before returning results. * @param {*} [options] Override http request option. * @throws {RequiredError} */ - retrieveRunCompare(id: string, checkpoint?: string, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.retrieveRunCompare(id, checkpoint, options).then((request) => request(axios, basePath)); + retrieveRunFiles(id: string, checkpoint?: string, pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.retrieveRunFiles(id, checkpoint, pageLimit, pageOffset, options).then((request) => request(axios, basePath)); }, /** * Returns token and cost usage broken down by stage and model for a specific run. @@ -273,15 +287,17 @@ export const RunOutputsApiFactory = function (configuration?: Configuration, bas */ export class RunOutputsApi extends BaseAPI { /** - * Returns file-level diffs produced by the run, optionally filtered to a specific checkpoint. - * @summary Retrieve Run Compare + * Returns a paginated list of file-level diffs produced by the run, optionally filtered to a specific checkpoint. + * @summary Retrieve Run Files * @param {string} id Unique run identifier (ULID). * @param {string} [checkpoint] Filter to a specific checkpoint ID. Omit to include all changes. + * @param {number} [pageLimit] Maximum number of items to return per page. + * @param {number} [pageOffset] Number of items to skip before returning results. * @param {*} [options] Override http request option. * @throws {RequiredError} */ - public retrieveRunCompare(id: string, checkpoint?: string, options?: RawAxiosRequestConfig) { - return RunOutputsApiFp(this.configuration).retrieveRunCompare(id, checkpoint, options).then((request) => request(this.axios, this.basePath)); + public retrieveRunFiles(id: string, checkpoint?: string, pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig) { + return RunOutputsApiFp(this.configuration).retrieveRunFiles(id, checkpoint, pageLimit, pageOffset, options).then((request) => request(this.axios, this.basePath)); } /** diff --git a/packages/arc-api-client/src/models/index.ts b/packages/arc-api-client/src/models/index.ts index f1317f366..d984acf45 100644 --- a/packages/arc-api-client/src/models/index.ts +++ b/packages/arc-api-client/src/models/index.ts @@ -54,6 +54,7 @@ export * from './paginated-api-question-list'; export * from './paginated-history-entry-list'; export * from './paginated-model-list'; export * from './paginated-retro-list'; +export * from './paginated-run-file-list'; export * from './paginated-run-list'; export * from './paginated-run-stage-list'; export * from './paginated-run-verification-list'; @@ -75,7 +76,6 @@ export * from './retro-stats'; export * from './root-response'; export * from './root-response-urls'; export * from './run-checkpoint'; -export * from './run-compare'; export * from './run-configuration'; export * from './run-error'; export * from './run-list-item'; diff --git a/packages/arc-api-client/src/models/run-compare.ts b/packages/arc-api-client/src/models/paginated-run-file-list.ts similarity index 51% rename from packages/arc-api-client/src/models/run-compare.ts rename to packages/arc-api-client/src/models/paginated-run-file-list.ts index d00ab723e..9e2ee6d1f 100644 --- a/packages/arc-api-client/src/models/run-compare.ts +++ b/packages/arc-api-client/src/models/paginated-run-file-list.ts @@ -13,28 +13,18 @@ */ -// May contain unused imports in some cases -// @ts-ignore -import type { DiffStats } from './diff-stats'; -// May contain unused imports in some cases -// @ts-ignore -import type { FileCheckpoint } from './file-checkpoint'; // May contain unused imports in some cases // @ts-ignore import type { FileDiff } from './file-diff'; +// May contain unused imports in some cases +// @ts-ignore +import type { PaginationMeta } from './pagination-meta'; /** - * File-level diff output for a run, with checkpoint filtering support. + * Paginated list of file diffs produced by a run. */ -export interface RunCompare { - /** - * Available checkpoints for filtering. - */ - 'checkpoints': Array; - /** - * File diffs, optionally filtered by checkpoint. - */ - 'files': Array; - 'stats': DiffStats; +export interface PaginatedRunFileList { + 'data': Array; + 'meta': PaginationMeta; }