-
@@ -120,36 +112,21 @@ class PipelineList extends Page<{}, PipelineListState> {
}
public async load(): Promise
{
- this._reload();
+ if (this._tableRef.current) {
+ this._tableRef.current.reload();
+ }
}
- private async _reload(loadRequest?: BaseListRequest): Promise {
- // Override the current state with incoming request
- const request: ListPipelinesRequest = Object.assign({
- orderAscending: this.state.orderAscending,
- pageSize: this.state.pageSize,
- pageToken: this.state.pageToken,
- sortBy: this.state.sortBy,
- }, loadRequest);
-
+ private async _reload(request: ListRequest): Promise {
let response: ApiListPipelinesResponse | null = null;
try {
response = await Apis.pipelineServiceApi.listPipelines(
- request.pageToken,
- request.pageSize,
- request.sortBy ? request.sortBy + (request.orderAscending ? ' asc' : ' desc') : ''
- );
+ request.pageToken, request.pageSize, request.sortBy);
} catch (err) {
await this.showPageError('Error: failed to retrieve list of pipelines.', err);
}
- this.setState({
- orderAscending: request.orderAscending!,
- pageSize: request.pageSize!,
- pageToken: request.pageToken!,
- pipelines: response ? response.pipelines || [] : [],
- sortBy: request.sortBy!,
- });
+ this.setState({ pipelines: response ? response.pipelines || [] : [] });
return response ? response.next_page_token || '' : '';
}
diff --git a/frontend/src/pages/PipelineSelector.tsx b/frontend/src/pages/PipelineSelector.tsx
index f15939e2b26..3f6f35294db 100644
--- a/frontend/src/pages/PipelineSelector.tsx
+++ b/frontend/src/pages/PipelineSelector.tsx
@@ -17,7 +17,7 @@
import * as React from 'react';
import CustomTable, { Column, Row } from '../components/CustomTable';
import Toolbar, { ToolbarActionConfig } from '../components/Toolbar';
-import { Apis, BaseListRequest, ListPipelinesRequest, PipelineSortKeys } from '../lib/Apis';
+import { Apis, ListRequest, PipelineSortKeys } from '../lib/Apis';
import { RouteComponentProps } from 'react-router-dom';
import { logger, formatDateString, errorToMessage } from '../lib/Utils';
import { ApiPipeline } from '../apis/pipeline';
@@ -29,9 +29,6 @@ interface PipelineSelectorProps extends RouteComponentProps {
}
interface PipelineSelectorState {
- orderAscending: boolean;
- pageSize: number;
- pageToken: string;
pipelines: ApiPipeline[];
selectedIds: string[];
sortBy: string;
@@ -39,14 +36,12 @@ interface PipelineSelectorState {
}
class PipelineSelector extends React.Component {
+ private _tableRef = React.createRef();
constructor(props: any) {
super(props);
this.state = {
- orderAscending: false,
- pageSize: 10,
- pageToken: '',
pipelines: [],
selectedIds: [],
sortBy: PipelineSortKeys.CREATED_AT,
@@ -55,7 +50,7 @@ class PipelineSelector extends React.Component
- { this._pipelineSelectionChanged(ids); this.setState({ selectedIds: ids });}} sortBy={sortBy}
+ { this._pipelineSelectionChanged(ids); this.setState({ selectedIds: ids }); }}
+ initialSortColumn={sortBy}
reload={this._loadPipelines.bind(this)} emptyMessage={'No pipelines found. Upload a pipeline and then try again.'} />
);
}
public async load() {
- await this._loadPipelines();
+ if (this._tableRef.current) {
+ this._tableRef.current.reload();
+ }
}
private _pipelineSelectionChanged(selectedIds: string[]): void {
@@ -98,23 +95,12 @@ class PipelineSelector extends React.Component {
- // Override the current state with incoming request
- const request: ListPipelinesRequest = Object.assign({
- orderAscending: this.state.orderAscending,
- pageSize: this.state.pageSize,
- pageToken: this.state.pageToken,
- sortBy: this.state.sortBy,
- }, loadRequest);
-
+ private async _loadPipelines(request: ListRequest): Promise {
let pipelines: ApiPipeline[] = [];
let nextPageToken = '';
try {
const response = await Apis.pipelineServiceApi.listPipelines(
- request.pageToken,
- request.pageSize,
- request.sortBy ? request.sortBy + (request.orderAscending ? ' asc' : ' desc') : '',
- );
+ request.pageToken, request.pageSize, request.sortBy);
pipelines = response.pipelines || [];
nextPageToken = response.next_page_token || '';
} catch (err) {
@@ -127,14 +113,7 @@ class PipelineSelector extends React.Component;
- orderAscending: boolean;
- pageSize: number;
- pageToken: string;
runs: ApiJob[];
selectedIds: string[];
sortBy: string;
@@ -45,15 +42,13 @@ interface RecurringRunListState {
}
class RecurringRunsManager extends React.Component {
+ private _tableRef = React.createRef();
constructor(props: any) {
super(props);
this.state = {
busyIds: new Set(),
- orderAscending: false,
- pageSize: 10,
- pageToken: '',
runs: [],
selectedIds: [],
sortBy: JobSortKeys.CREATED_AT,
@@ -62,7 +57,7 @@ class RecurringRunsManager extends React.Component
- this.setState({ selectedIds: ids })} sortBy={sortBy}
+ this.setState({ selectedIds: ids })} initialSortColumn={sortBy}
reload={this._loadRuns.bind(this)} emptyMessage={'No recurring runs found in this experiment.'} />
);
}
public async load() {
- await this._loadRuns();
+ if (this._tableRef.current) {
+ this._tableRef.current.reload();
+ }
}
- private async _loadRuns(loadRequest?: BaseListRequest): Promise {
- // Override the current state with incoming request
- const request: ListJobsRequest = Object.assign({
- experimentId: this.props.experimentId,
- orderAscending: this.state.orderAscending,
- pageSize: this.state.pageSize,
- pageToken: this.state.pageToken,
- sortBy: this.state.sortBy,
- }, loadRequest);
-
+ private async _loadRuns(request: ListRequest): Promise {
let runs: ApiJob[] = [];
let nextPageToken = '';
try {
const response = await Apis.jobServiceApi.listJobs(
request.pageToken,
request.pageSize,
- request.sortBy ? request.sortBy + (request.orderAscending ? ' asc' : ' desc') : '',
+ request.sortBy,
+ ApiResourceType.EXPERIMENT.toString(),
+ this.props.experimentId,
);
runs = response.jobs || [];
nextPageToken = response.next_page_token || '';
@@ -130,14 +120,7 @@ class RecurringRunsManager extends React.Component {
pipeline_runtime: { workflow_manifest: '' },
run: {},
} as ApiRunDetail),
- listRuns: () => Promise.resolve({
+ listRuns: jest.fn(() => Promise.resolve({
runs: [{
id: 'testrun1',
name: 'test run1',
} as ApiRun],
- }),
+ })),
};
const props = generateProps();
const tree = shallow();
- await (tree.instance() as RunList).refresh();
+ await (tree.instance() as any)._loadRuns({});
+ expect(Apis.runServiceApi.listRuns).toHaveBeenLastCalledWith(undefined, undefined, undefined, undefined, undefined);
expect(props.onError).not.toHaveBeenCalled();
expect(tree).toMatchSnapshot();
});
@@ -67,7 +68,7 @@ describe('RunList', () => {
};
const props = generateProps();
const tree = shallow();
- await (tree.instance() as RunList).refresh();
+ await (tree.instance() as any)._loadRuns({});
expect(props.onError).toHaveBeenLastCalledWith('Error: failed to fetch runs.', 'bad stuff');
});
@@ -88,7 +89,7 @@ describe('RunList', () => {
};
const props = generateProps();
const tree = shallow();
- await (tree.instance() as RunList).refresh();
+ await (tree.instance() as any)._loadRuns({});
expect(tree).toMatchSnapshot();
});
@@ -105,7 +106,7 @@ describe('RunList', () => {
const props = generateProps();
props.runIdListMask = ['run1', 'run2'];
const tree = shallow();
- await (tree.instance() as RunList).refresh();
+ await (tree.instance() as any)._loadRuns({});
expect(tree).toMatchSnapshot();
});
@@ -132,29 +133,28 @@ describe('RunList', () => {
};
const props = generateProps();
const tree = shallow();
- await (tree.instance() as RunList).refresh();
+ await (tree.instance() as any)._loadRuns({});
expect(props.onError).not.toHaveBeenCalled();
expect(tree).toMatchSnapshot();
});
it('loads runs for a given experiment id', async () => {
- const listRunsSpy = jest.fn(() => Promise.resolve({
- runs: [{ id: 'testRun1' }],
- }));
(Apis as any).runServiceApi = {
getRun: () => Promise.resolve({
pipeline_runtime: { workflow_manifest: '' },
run: {},
} as ApiRunDetail),
- listRuns: listRunsSpy,
+ listRuns: jest.fn(() => Promise.resolve({
+ runs: [{ id: 'testRun1' }],
+ })),
};
const props = generateProps();
props.experimentIdMask = 'experiment1';
const tree = shallow();
- await (tree.instance() as RunList).refresh();
+ await (tree.instance() as any)._loadRuns({ pageSize: 10, pageToken: '', orderAscending: true });
expect(props.onError).not.toHaveBeenCalled();
- expect(listRunsSpy).toHaveBeenLastCalledWith(
- '', 10, 'created_at desc', ApiResourceType.EXPERIMENT.toString(), 'experiment1');
+ expect(Apis.runServiceApi.listRuns).toHaveBeenLastCalledWith(
+ '', 10, undefined, ApiResourceType.EXPERIMENT.toString(), 'experiment1');
expect(tree).toMatchSnapshot();
});
@@ -171,7 +171,7 @@ describe('RunList', () => {
const props = generateProps();
props.runIdListMask = ['run1', 'run2'];
const tree = shallow();
- await (tree.instance() as RunList).refresh();
+ await (tree.instance() as any)._loadRuns({});
expect(props.onError).not.toHaveBeenCalled();
expect(listRunsSpy).not.toHaveBeenCalled();
expect(getRunSpy).toHaveBeenCalledWith('run1');
diff --git a/frontend/src/pages/RunList.tsx b/frontend/src/pages/RunList.tsx
index 531a4b4bec7..65629120b7a 100644
--- a/frontend/src/pages/RunList.tsx
+++ b/frontend/src/pages/RunList.tsx
@@ -17,7 +17,7 @@
import * as React from 'react';
import CustomTable, { Column, Row } from '../components/CustomTable';
import RunUtils from '../../src/lib/RunUtils';
-import { Apis, RunSortKeys, BaseListRequest, ListRunsRequest } from '../lib/Apis';
+import { Apis, RunSortKeys, ListRequest } from '../lib/Apis';
import { ApiListRunsResponse, ApiRunDetail, ApiRun, ApiResourceType, RunMetricFormat, ApiRunMetric } from '../../src/apis/run';
import { Link, RouteComponentProps } from 'react-router-dom';
import { NodePhase, statusToIcon } from './Status';
@@ -70,23 +70,18 @@ export interface RunListProps extends RouteComponentProps {
interface RunListState {
metrics: MetricMetadata[];
- orderAscending: boolean;
- pageSize: number;
- pageToken: string;
runs: DisplayRun[];
sortBy: string;
}
class RunList extends React.Component {
+ private _tableRef = React.createRef();
constructor(props: any) {
super(props);
this.state = {
metrics: [],
- orderAscending: false,
- pageSize: 10,
- pageToken: '',
runs: [],
sortBy: RunSortKeys.CREATED_AT,
};
@@ -157,21 +152,21 @@ class RunList extends React.Component {
return row;
});
- return (
-
-
-
- );
+ return (
+
+
);
}
public async refresh() {
- await this._loadRuns();
+ if (this._tableRef.current) {
+ this._tableRef.current.reload();
+ }
}
private _metricBufferCustomRenderer() {
@@ -199,15 +194,15 @@ class RunList extends React.Component {
if (displayMetric.metric.number_value - displayMetric.metadata.minValue < 0) {
logger.error(`Run ${arguments[1]}'s metric ${displayMetric.metadata.name}'s value:`
- + ` ${displayMetric.metric.number_value}) was lower than the supposed minimum of`
- + ` ${displayMetric.metadata.minValue})`);
+ + ` ${displayMetric.metric.number_value}) was lower than the supposed minimum of`
+ + ` ${displayMetric.metadata.minValue})`);
return {displayString}
;
}
if (displayMetric.metadata.maxValue - displayMetric.metric.number_value < 0) {
logger.error(`Run ${arguments[1]}'s metric ${displayMetric.metadata.name}'s value:`
- + ` ${displayMetric.metric.number_value}) was greater than the supposed maximum of`
- + ` ${displayMetric.metadata.maxValue})`);
+ + ` ${displayMetric.metric.number_value}) was greater than the supposed maximum of`
+ + ` ${displayMetric.metadata.maxValue})`);
return {displayString}
;
}
@@ -227,7 +222,7 @@ class RunList extends React.Component {
);
}
- private async _loadRuns(loadRequest?: BaseListRequest): Promise {
+ private async _loadRuns(loadRequest: ListRequest): Promise {
if (Array.isArray(this.props.runIdListMask)) {
return await this._loadSpecificRuns(this.props.runIdListMask);
}
@@ -254,24 +249,15 @@ class RunList extends React.Component {
return '';
}
- private async _loadAllRuns(loadRequest?: BaseListRequest): Promise {
- // Override the current state with incoming request
- const request: ListRunsRequest = Object.assign({
- experimentId: this.props.experimentIdMask,
- orderAscending: this.state.orderAscending,
- pageSize: this.state.pageSize,
- pageToken: this.state.pageToken,
- sortBy: this.state.sortBy,
- }, loadRequest);
-
+ private async _loadAllRuns(request: ListRequest): Promise {
let response: ApiListRunsResponse;
try {
response = await Apis.runServiceApi.listRuns(
request.pageToken,
request.pageSize,
- request.sortBy ? request.sortBy + (request.orderAscending ? ' asc' : ' desc') : '',
- request.experimentId ? ApiResourceType.EXPERIMENT.toString() : undefined,
- request.experimentId,
+ request.sortBy,
+ this.props.experimentIdMask ? ApiResourceType.EXPERIMENT.toString() : undefined,
+ this.props.experimentIdMask,
);
} catch (err) {
this.props.onError('Error: failed to fetch runs.', err);
@@ -299,9 +285,6 @@ class RunList extends React.Component {
this.setState({
metrics: this._extractMetricMetadata(displayRuns),
- orderAscending: request.orderAscending!,
- pageSize: request.pageSize!,
- pageToken: request.pageToken!,
runs: displayRuns,
sortBy: request.sortBy!,
});
@@ -318,17 +301,17 @@ class RunList extends React.Component {
return await Promise.all(
displayRuns.map(async (displayRun) => {
const pipelineId = RunUtils.getPipelineId(displayRun.metadata);
- if (pipelineId) {
- try {
- const pipeline = await Apis.pipelineServiceApi.getPipeline(pipelineId);
- displayRun.pipeline = { displayName: pipeline.name || '', id: pipelineId };
- } catch (err) {
- // This could be an API exception, or a JSON parse exception.
- displayRun.error = await errorToMessage(err);
- }
+ if (pipelineId) {
+ try {
+ const pipeline = await Apis.pipelineServiceApi.getPipeline(pipelineId);
+ displayRun.pipeline = { displayName: pipeline.name || '', id: pipelineId };
+ } catch (err) {
+ // This could be an API exception, or a JSON parse exception.
+ displayRun.error = await errorToMessage(err);
}
- return displayRun;
- })
+ }
+ return displayRun;
+ })
);
}
@@ -339,7 +322,7 @@ class RunList extends React.Component {
}
return (
e.stopPropagation()}
- to={RoutePage.PIPELINE_DETAILS.replace(':' + RouteParams.pipelineId, pipelineInfo.id)}>
+ to={RoutePage.PIPELINE_DETAILS.replace(':' + RouteParams.pipelineId, pipelineInfo.id)}>
{pipelineInfo.displayName}
);
@@ -355,20 +338,20 @@ class RunList extends React.Component {
return await Promise.all(
displayRuns.map(async (displayRun) => {
const experimentId = RunUtils.getFirstExperimentReferenceId(displayRun.metadata);
- if (experimentId) {
- try {
- // TODO: Experiment could be an optional field in state since whenever the RunList is
- // created from the ExperimentDetails page, we already have the experiment (and will)
- // be fetching the same one over and over here.
- const experiment = await Apis.experimentServiceApi.getExperiment(experimentId);
- displayRun.experiment = { displayName: experiment.name || '', id: experimentId };
- } catch (err) {
- // This could be an API exception, or a JSON parse exception.
- displayRun.error = await errorToMessage(err);
- }
+ if (experimentId) {
+ try {
+ // TODO: Experiment could be an optional field in state since whenever the RunList is
+ // created from the ExperimentDetails page, we already have the experiment (and will)
+ // be fetching the same one over and over here.
+ const experiment = await Apis.experimentServiceApi.getExperiment(experimentId);
+ displayRun.experiment = { displayName: experiment.name || '', id: experimentId };
+ } catch (err) {
+ // This could be an API exception, or a JSON parse exception.
+ displayRun.error = await errorToMessage(err);
}
- return displayRun;
- })
+ }
+ return displayRun;
+ })
);
}
@@ -379,7 +362,7 @@ class RunList extends React.Component {
}
return (
e.stopPropagation()}
- to={RoutePage.EXPERIMENT_DETAILS.replace(':' + RouteParams.experimentId, experimentInfo.id)}>
+ to={RoutePage.EXPERIMENT_DETAILS.replace(':' + RouteParams.experimentId, experimentInfo.id)}>
{experimentInfo.displayName}
);
diff --git a/frontend/src/pages/__snapshots__/RunList.test.tsx.snap b/frontend/src/pages/__snapshots__/RunList.test.tsx.snap
index 365bc7190f2..e2ee6cb1fac 100644
--- a/frontend/src/pages/__snapshots__/RunList.test.tsx.snap
+++ b/frontend/src/pages/__snapshots__/RunList.test.tsx.snap
@@ -38,8 +38,7 @@ exports[`RunList displays error in run row if it failed to parse (run list mask)
]
}
emptyMessage="No runs found."
- orderAscending={false}
- pageSize={10}
+ initialSortColumn="created_at"
reload={[Function]}
rows={
Array [
@@ -69,7 +68,6 @@ exports[`RunList displays error in run row if it failed to parse (run list mask)
},
]
}
- sortBy="created_at"
/>
`;
@@ -112,8 +110,6 @@ exports[`RunList displays error in run row if it failed to parse 1`] = `
]
}
emptyMessage="No runs found."
- orderAscending={false}
- pageSize={10}
reload={[Function]}
rows={
Array [
@@ -131,7 +127,6 @@ exports[`RunList displays error in run row if it failed to parse 1`] = `
},
]
}
- sortBy="created_at"
/>