Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add error state when pipeline version is deleted #2915

Merged
merged 1 commit into from
Jul 10, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@ class PipelineRunDetails extends RunDetails {
findOutputArtifacts() {
return cy.findByTestId('Output-artifacts');
}

findErrorState(id: string) {
return cy.findByTestId(id);
}
}

export const pipelineDetails = new PipelineDetails();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import {
buildMockRecurringRunKF,
} from '~/__mocks__';
import {
activeRunsTable,
archivedRunsTable,
archiveExperimentModal,
bulkArchiveExperimentModal,
bulkRestoreExperimentModal,
pipelineRunDetails,
pipelineRecurringRunTable,
pipelineRunsGlobal,
restoreExperimentModal,
Expand All @@ -27,7 +29,7 @@ import {
ProjectModel,
RouteModel,
} from '~/__tests__/cypress/cypress/utils/models';
import { RecurringRunStatus, StorageStateKF } from '~/concepts/pipelines/kfTypes';
import { RecurringRunStatus, RuntimeStateKF, StorageStateKF } from '~/concepts/pipelines/kfTypes';

const projectName = 'test-project-name';
const initialMockPipeline = buildMockPipelineV2({ display_name: 'Test pipeline' });
Expand All @@ -51,6 +53,14 @@ const mockExperiments = [
}),
];

const mockActiveRuns = buildMockRunKF({
display_name: 'Test active run 4',
run_id: 'run-4',
experiment_id: 'test-experiment-1',
created_at: '2024-02-10T00:00:00Z',
state: RuntimeStateKF.SUCCEEDED,
});

describe('Experiments', () => {
describe('Active experiments', () => {
beforeEach(() => {
Expand Down Expand Up @@ -251,6 +261,41 @@ describe('Experiments', () => {
cy.findByLabelText('Experiment').contains(mockExperiment.display_name);
});

it('should display error state when the pipeline version deleted', () => {
cy.interceptOdh(
'GET /api/service/pipelines/:namespace/:serviceName/apis/v2beta1/runs/:runId',
{
path: {
namespace: projectName,
serviceName: 'dspa',
runId: mockActiveRuns.run_id,
},
},
mockActiveRuns,
);
activeRunsTable.getRowByName('Test active run 4').findColumnName('Test active run 4').click();
pipelineRunDetails
.findErrorState('run-graph-error-state')
.should('have.text', 'Pipeline run graph unavailable');

pipelineRunDetails.findDetailsTab().click();
pipelineRunDetails.findDetailItem('Name').findValue().contains(mockActiveRuns.display_name);
pipelineRunDetails
.findDetailItem('Pipeline version')
.findValue()
.contains('No pipeline version');
pipelineRunDetails.findDetailItem('Project').findValue().contains(projectName);
pipelineRunDetails.findDetailItem('Run ID').findValue().contains(mockActiveRuns.run_id);
pipelineRunDetails
.findDetailItem('Workflow name')
.findValue()
.contains(mockActiveRuns.display_name);
pipelineRunDetails.findPipelineSpecTab().click();
pipelineRunDetails
.findErrorState('pipeline-spec-error-state')
.should('have.text', 'Pipeline spec unavailable');
});

it('navigates back to experiments from "Create run" page breadcrumb', () => {
pipelineRunsGlobal.findCreateRunButton().click();
cy.findByLabelText('Breadcrumb').findByText(`Experiments - ${projectName}`).click();
Expand Down Expand Up @@ -370,7 +415,7 @@ const initIntercepts = () => {
{
path: { namespace: projectName, serviceName: 'dspa' },
},
{ runs: [] },
{ runs: [mockActiveRuns] },
);

cy.interceptOdh(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const DashboardCodeEditor: React.FC<Partial<DashboardCodeEditorProps>> = ({
height = 'calc(100% - 38px)',
...props
}) => (
<div data-testid={props.testId} style={{ height }}>
<div data-testid={props.testId} style={{ height, padding: '14px' }}>
<CodeEditor height="100%" className="odh-dashboard__code-editor" {...props} />
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,29 @@ import {
} from '@patternfly/react-core';
import { ExclamationCircleIcon } from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon';
import DashboardCodeEditor from '~/concepts/dashboard/codeEditor/DashboardCodeEditor';
import PipelineVersionError from './PipelineVersionError';

type PipelineDetailsYAMLProps = {
filename?: string;
content?: Record<string, unknown> | null;
versionError?: Error;
};

const PipelineDetailsYAML: React.FC<PipelineDetailsYAMLProps> = ({ filename, content }) => {
const PipelineDetailsYAML: React.FC<PipelineDetailsYAMLProps> = ({
versionError,
filename,
content,
}) => {
if (versionError) {
return (
<PipelineVersionError
title="Pipeline spec unavailable"
description="The pipeline version that this pipeline spec belongs to has been deleted."
testId="pipeline-spec-error-state"
/>
);
}

if (!content) {
return (
<EmptyState>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
EmptyState,
EmptyStateBody,
EmptyStateHeader,
EmptyStateIcon,
EmptyStateVariant,
PageSection,
} from '@patternfly/react-core';
import { ExclamationTriangleIcon } from '@patternfly/react-icons';
import React from 'react';

type PipelineVersionErrorProps = {
title?: string;
description?: string;
testId?: string;
};

const PipelineVersionError: React.FC<PipelineVersionErrorProps> = ({
title,
description,
testId,
}) => (
<PageSection className="pf-v5-u-h-100">
<EmptyState variant={EmptyStateVariant.lg} isFullHeight>
<EmptyStateHeader
data-testid={testId}
titleText={title}
icon={
<EmptyStateIcon
color="var(--pf-v5-global--warning-color--100)"
icon={ExclamationTriangleIcon}
/>
}
headingLevel="h2"
/>
<EmptyStateBody>{description}</EmptyStateBody>
</EmptyState>
</PageSection>
);

export default PipelineVersionError;
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ const PipelineDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath }) =
hidden={PipelineDetailsTab.YAML !== activeTabKey}
className="pf-v5-u-h-100"
>
<TabContentBody hasPadding className="pf-v5-u-h-100">
<TabContentBody className="pf-v5-u-h-100">
<PipelineDetailsYAML
filename={`Pipeline ${
getCorePipelineSpec(pipelineVersion?.pipeline_spec)?.pipelineInfo.name ??
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ const PipelineRunDetails: React.FC<
[selectedId, nodes],
);

const loaded = runLoaded && (versionLoaded || !!run?.pipeline_spec);
const error = versionError || runError;
const loaded = runLoaded && (versionLoaded || !!run?.pipeline_spec || !!versionError);
const error = runError;

if (error) {
return (
Expand Down Expand Up @@ -150,10 +150,12 @@ const PipelineRunDetails: React.FC<
>
<PipelineRunDetailsTabs
run={run}
versionError={versionError}
pipelineSpec={version?.pipeline_spec}
graphContent={
<PipelineTopology
nodes={nodes}
versionError={versionError}
selectedIds={selectedId ? [selectedId] : []}
ppadti marked this conversation as resolved.
Show resolved Hide resolved
onSelectionChange={(ids) => {
const firstId = ids[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ interface PipelineRunDetailsTabsProps {
run: PipelineRunKFv2 | PipelineRecurringRunKFv2 | null;
pipelineSpec: PipelineSpecVariable | undefined;
graphContent: React.ReactNode;
versionError?: Error;
}

export const PipelineRunDetailsTabs: React.FC<PipelineRunDetailsTabsProps> = ({
run,
pipelineSpec,
graphContent,
versionError,
}) => {
const [activeKey, setActiveKey] = React.useState<string | number>(DetailsTabKey.Graph);
const isRecurringRun = run && isPipelineRecurringRun(run);
Expand Down Expand Up @@ -57,7 +59,7 @@ export const PipelineRunDetailsTabs: React.FC<PipelineRunDetailsTabsProps> = ({
</TabContentBody>
</Tab>

{!isRecurringRun && pipelineSpec && (
{!isRecurringRun && (
<Tab
eventKey={DetailsTabKey.Spec}
tabContentId={DetailsTabKey.Spec}
Expand All @@ -73,6 +75,7 @@ export const PipelineRunDetailsTabs: React.FC<PipelineRunDetailsTabsProps> = ({
id={DetailsTabKey.Graph}
eventKey={DetailsTabKey.Graph}
className="pf-v5-u-h-100"
data-testid="pipeline-graph-tab"
>
<TabContentBody className="pf-v5-u-h-100">{graphContent}</TabContentBody>
</TabContent>
Expand All @@ -83,9 +86,14 @@ export const PipelineRunDetailsTabs: React.FC<PipelineRunDetailsTabsProps> = ({
eventKey={DetailsTabKey.Spec}
hidden={activeKey !== DetailsTabKey.Spec}
style={{ flex: 1 }}
data-testid="pipeline-spec-tab"
>
<TabContentBody className="pf-v5-u-h-100" hasPadding>
<PipelineDetailsYAML filename={run?.display_name} content={pipelineSpec} />
<TabContentBody className="pf-v5-u-h-100">
<PipelineDetailsYAML
filename={run?.display_name}
content={pipelineSpec}
versionError={versionError}
/>
</TabContentBody>
</TabContent>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ const PipelineRunTabDetails: React.FC<PipelineRunTabDetailsProps> = ({ run, work
),
},
]
: versionError
? [{ key: 'Pipeline version', value: 'No pipeline version' }]
: []),
...(pipeline
? [
Expand Down
15 changes: 14 additions & 1 deletion frontend/src/concepts/topology/PipelineTopology.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,23 @@ import {
VisualizationProvider,
} from '@patternfly/react-topology';
import { Bullseye, Spinner } from '@patternfly/react-core';
import PipelineVersionError from '~/concepts/pipelines/content/pipelinesDetails/PipelineVersionError';
import PipelineTopologyEmpty from './PipelineTopologyEmpty';
import useTopologyController from './useTopologyController';
import PipelineVisualizationSurface from './PipelineVisualizationSurface';
import PipelineTopologyEmpty from './PipelineTopologyEmpty';

type PipelineTopologyProps = {
selectedIds?: string[];
onSelectionChange?: (selectionIds: string[]) => void;
nodes: PipelineNodeModel[];
versionError?: Error;
};

const PipelineTopology: React.FC<PipelineTopologyProps> = ({
nodes,
selectedIds,
onSelectionChange,
versionError,
}) => {
const controller = useTopologyController('g1');

Expand All @@ -37,6 +40,16 @@ const PipelineTopology: React.FC<PipelineTopologyProps> = ({
return undefined;
}, [controller, onSelectionChange]);

if (versionError) {
ppadti marked this conversation as resolved.
Show resolved Hide resolved
return (
<PipelineVersionError
title="Pipeline run graph unavailable"
description="The pipeline version that this run graph belongs to has been deleted."
testId="run-graph-error-state"
/>
);
}

if (!nodes.length) {
return <PipelineTopologyEmpty />;
}
Expand Down
Loading