Skip to content

Commit

Permalink
Add pipeline url submit modal
Browse files Browse the repository at this point in the history
Add code source URL to pipeline import modal

Refactor pipeline import and version import modals

Refactor PipelineUploadRadio component

Remove code source input from PipelineImportModal and PipelineVersionImportModal

fix test

Remove unused imports and descriptions in Pipelines.cy.ts

Add pipeline version description

Update pipeline version description
  • Loading branch information
Gkrumbach07 committed Mar 14, 2024
1 parent 2859b99 commit d127a4b
Show file tree
Hide file tree
Showing 13 changed files with 440 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,53 @@ describe('Pipelines', () => {
pipelinesTable.findRowByName('New pipeline');
});

it('imports a new pipeline by url', () => {
const createPipelineAndVersionParams = {
pipeline: {
display_name: 'New pipeline',
},
pipeline_version: {
display_name: 'New pipeline',
package_url: {
pipeline_url: 'https://example.com/pipeline.yaml',
},
},
};
const createdMockPipeline = buildMockPipelineV2(createPipelineAndVersionParams.pipeline);

// Intercept upload/re-fetch of pipelines
pipelineImportModal
.mockCreatePipelineAndVersion(createPipelineAndVersionParams)
.as('createPipelineAndVersion');
pipelinesTable
.mockGetPipelines([initialMockPipeline, createdMockPipeline])
.as('refreshPipelines');
pipelinesTable.mockGetPipelineVersions(
[buildMockPipelineVersionV2(createPipelineAndVersionParams.pipeline_version)],
'new-pipeline',
);

// Wait for the pipelines table to load
pipelinesTable.find();

// Open the "Import pipeline" modal
pipelinesGlobal.findImportPipelineButton().click();

// Fill out the "Import pipeline" modal and submit
pipelineImportModal.shouldBeOpen();
pipelineImportModal.fillPipelineName('New pipeline');
pipelineImportModal.findImportPipelineRadio().check();
pipelineImportModal.findPipelineUrlInput().type('https://example.com/pipeline.yaml');
pipelineImportModal.submit();

// Wait for upload/fetch requests
cy.wait('@createPipelineAndVersion');
cy.wait('@refreshPipelines');

// Verify the uploaded pipeline is in the table
pipelinesTable.findRowByName('New pipeline');
});

it('uploads a new pipeline version', () => {
const uploadVersionParams = {
display_name: 'New pipeline version',
Expand Down Expand Up @@ -137,6 +184,50 @@ describe('Pipelines', () => {
pipelinesTable.findRowByName('New pipeline version');
});

it('imports a new pipeline version by url', () => {
const createPipelineVersionParams = {
pipeline_id: 'test-pipeline',
display_name: 'New pipeline version',
description: 'New pipeline description',
package_url: {
pipeline_url: 'https://example.com/pipeline.yaml',
},
};
// Wait for the pipelines table to load
pipelinesTable.find();

// Open the "Upload new version" modal
pipelinesGlobal.findUploadVersionButton().click();

// Intercept upload/re-fetch of pipeline versions
pipelinesTable
.mockGetPipelineVersions(
[initialMockPipelineVersion, buildMockPipelineVersionV2(createPipelineVersionParams)],
initialMockPipeline.pipeline_id,
)
.as('refreshVersions');

pipelineVersionImportModal
.mockCreatePipelineVersion(createPipelineVersionParams)
.as('createVersion');

// Fill out the "Upload new version" modal and submit
pipelineVersionImportModal.shouldBeOpen();
pipelineVersionImportModal.selectPipelineByName('Test pipeline');
pipelineVersionImportModal.fillVersionName('New pipeline version');
pipelineVersionImportModal.findImportPipelineRadio().check();
pipelineVersionImportModal.findPipelineUrlInput().type('https://example.com/pipeline.yaml');
pipelineVersionImportModal.submit();

// Wait for upload/fetch requests
cy.wait('@createVersion');
cy.wait('@refreshVersions');

// Verify the uploaded pipeline version is in the table
pipelinesTable.toggleExpandRowByIndex(0);
pipelinesTable.findRowByName('New pipeline version');
});

it('delete a single pipeline version', () => {
createDeleteVersionIntercept(
initialMockPipelineVersion.pipeline_id,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PipelineKFv2 } from '~/concepts/pipelines/kfTypes';
import { CreatePipelineAndVersionKFData, PipelineKFv2 } from '~/concepts/pipelines/kfTypes';
import { buildMockPipelineV2 } from '~/__mocks__/mockPipelinesProxy';
import { Modal } from '~/__tests__/cypress/cypress/pages/components/Modal';

Expand Down Expand Up @@ -27,6 +27,18 @@ class PipelineImportModal extends Modal {
return this.findFooter().findByRole('button', { name: 'Import pipeline' });
}

findUploadPipelineRadio() {
return this.find().findByTestId('upload-file-radio');
}

findImportPipelineRadio() {
return this.find().findByTestId('import-url-radio');
}

findPipelineUrlInput() {
return this.find().findByTestId('pipeline-url-input');
}

fillPipelineName(value: string) {
this.findPipelineNameInput().clear().type(value);
}
Expand All @@ -39,6 +51,17 @@ class PipelineImportModal extends Modal {
this.findUploadPipelineInput().selectFile([filePath], { force: true });
}

mockCreatePipelineAndVersion(params: CreatePipelineAndVersionKFData) {
return cy.intercept(
{
method: 'POST',
pathname: '/api/proxy/apis/v2beta1/pipelines/create',
times: 1,
},
buildMockPipelineV2(params.pipeline),
);
}

mockUploadPipeline(params: Partial<PipelineKFv2>) {
return cy.intercept(
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PipelineVersionKFv2 } from '~/concepts/pipelines/kfTypes';
import { CreatePipelineVersionKFData, PipelineVersionKFv2 } from '~/concepts/pipelines/kfTypes';
import { buildMockPipelineVersionV2 } from '~/__mocks__/mockPipelineVersionsProxy';
import { Modal } from '~/__tests__/cypress/cypress/pages/components/Modal';

Expand Down Expand Up @@ -38,6 +38,22 @@ class PipelineImportModal extends Modal {
this.findUploadPipelineInput().selectFile([filePath], { force: true });
}

findUploadPipelineRadio() {
return this.find().findByTestId('upload-file-radio');
}

findImportPipelineRadio() {
return this.find().findByTestId('import-url-radio');
}

findPipelineUrlInput() {
return this.find().findByTestId('pipeline-url-input');
}

findCodeSourceInput() {
return this.find().findByTestId('code-source-input');
}

selectPipelineByName(name: string) {
this.findPipelineSelect()
.click()
Expand All @@ -58,6 +74,17 @@ class PipelineImportModal extends Modal {
this.findSubmitButton().click();
}

mockCreatePipelineVersion(params: CreatePipelineVersionKFData) {
return cy.intercept(
{
method: 'POST',
pathname: `/api/proxy/apis/v2beta1/pipelines/${params.pipeline_id}/versions`,
times: 1,
},
buildMockPipelineVersionV2(params),
);
}

mockUploadVersion(params: Partial<PipelineVersionKFv2>) {
return cy.intercept(
{
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/api/pipelines/callTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
GetPipelineVersion,
DeletePipelineVersion,
ListPipelineVersions,
CreatePipelineAndVersion,
CreatePipelineVersion,
} from '~/concepts/pipelines/types';
import { K8sAPIOptions } from '~/k8sTypes';

Expand All @@ -28,6 +30,8 @@ import { K8sAPIOptions } from '~/k8sTypes';
type KubeflowSpecificAPICall = (opts: K8sAPIOptions, ...args: any[]) => Promise<unknown>;
type KubeflowAPICall<ActualCall extends KubeflowSpecificAPICall> = (hostPath: string) => ActualCall;

export type CreatePipelineVersionAPI = KubeflowAPICall<CreatePipelineVersion>;
export type CreatePipelineAndVersionAPI = KubeflowAPICall<CreatePipelineAndVersion>;
export type CreateExperimentAPI = KubeflowAPICall<CreateExperiment>;
export type CreatePipelineRunAPI = KubeflowAPICall<CreatePipelineRun>;
export type CreatePipelineRunJobAPI = KubeflowAPICall<CreatePipelineRunJob>;
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/api/pipelines/custom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
GetPipelineVersionAPI,
ListPipelineVersionsAPI,
UpdatePipelineRunAPI,
CreatePipelineAndVersionAPI,
CreatePipelineVersionAPI,
} from './callTypes';
import { handlePipelineFailures } from './errorUtils';

Expand All @@ -47,6 +49,15 @@ const pipelineParamsToQuery = (params?: PipelineParams) => ({
export const createExperiment: CreateExperimentAPI = (hostPath) => (opts, data) =>
handlePipelineFailures(proxyCREATE(hostPath, `/apis/v2beta1/experiments`, data, {}, opts));

export const createPipelineAndVersion: CreatePipelineAndVersionAPI = (hostPath) => (opts, data) =>
handlePipelineFailures(proxyCREATE(hostPath, `/apis/v2beta1/pipelines/create`, data, {}, opts));

export const createPipelineVersion: CreatePipelineVersionAPI =
(hostPath) => (opts, pipelineId, data) =>
handlePipelineFailures(
proxyCREATE(hostPath, `/apis/v2beta1/pipelines/${pipelineId}/versions`, data, {}, opts),
);

export const createPipelineRun: CreatePipelineRunAPI = (hostPath) => (opts, data) =>
handlePipelineFailures(proxyCREATE(hostPath, `/apis/v2beta1/runs`, data, {}, opts));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import {
Modal,
Stack,
StackItem,
TextArea,
TextInput,
} from '@patternfly/react-core';
import { usePipelinesAPI } from '~/concepts/pipelines/context';
import { usePipelineImportModalData } from '~/concepts/pipelines/content/import/useImportModalData';
import { getProjectDisplayName } from '~/pages/projects/utils';
import PipelineFileUpload from '~/concepts/pipelines/content/import/PipelineFileUpload';
import { PipelineKFv2 } from '~/concepts/pipelines/kfTypes';
import PipelineUploadRadio from './PipelineUploadRadio';
import { PipelineUploadOption } from './utils';

type PipelineImportModalProps = {
isOpen: boolean;
Expand All @@ -24,9 +26,14 @@ const PipelineImportModal: React.FC<PipelineImportModalProps> = ({ isOpen, onClo
const { project, api, apiAvailable } = usePipelinesAPI();
const [importing, setImporting] = React.useState(false);
const [error, setError] = React.useState<Error | undefined>();
const [{ name, description, fileContents }, setData, resetData] = usePipelineImportModalData();
const [{ name, description, fileContents, pipelineUrl, uploadOption }, setData, resetData] =
usePipelineImportModalData();

const isImportButtonDisabled = !apiAvailable || importing || !name || !fileContents;
const isImportButtonDisabled =
!apiAvailable ||
importing ||
!name ||
(uploadOption === PipelineUploadOption.URL_IMPORT ? !pipelineUrl : !fileContents);

const onBeforeClose = (pipeline?: PipelineKFv2) => {
onClose(pipeline);
Expand All @@ -35,6 +42,46 @@ const PipelineImportModal: React.FC<PipelineImportModalProps> = ({ isOpen, onClo
resetData();
};

const onSubmit = () => {
setImporting(true);
setError(undefined);

if (uploadOption === PipelineUploadOption.FILE_UPLOAD) {
api
.uploadPipeline({}, name, description, fileContents)
.then((pipeline) => onBeforeClose(pipeline))
.catch((e) => {
setImporting(false);
setError(e);
});
} else {
api
.createPipelineAndVersion(
{},
{
pipeline: {
/* eslint-disable camelcase */
display_name: name,
description,
},
pipeline_version: {
display_name: name,
description,
package_url: {
pipeline_url: pipelineUrl,
},
/* eslint-enable camelcase */
},
},
)
.then((pipeline) => onBeforeClose(pipeline))
.catch((e) => {
setImporting(false);
setError(e);
});
}
};

return (
<Modal
title="Import pipeline"
Expand All @@ -46,17 +93,7 @@ const PipelineImportModal: React.FC<PipelineImportModalProps> = ({ isOpen, onClo
variant="primary"
isDisabled={isImportButtonDisabled}
isLoading={importing}
onClick={() => {
setImporting(true);
setError(undefined);
api
.uploadPipeline({}, name, description, fileContents)
.then((pipeline) => onBeforeClose(pipeline))
.catch((e) => {
setImporting(false);
setError(e);
});
}}
onClick={onSubmit}
>
Import pipeline
</Button>,
Expand Down Expand Up @@ -88,7 +125,7 @@ const PipelineImportModal: React.FC<PipelineImportModalProps> = ({ isOpen, onClo
</StackItem>
<StackItem>
<FormGroup label="Pipeline description" fieldId="pipeline-description">
<TextInput
<TextArea
isRequired
type="text"
id="pipeline-description"
Expand All @@ -99,9 +136,15 @@ const PipelineImportModal: React.FC<PipelineImportModalProps> = ({ isOpen, onClo
</FormGroup>
</StackItem>
<StackItem>
<PipelineFileUpload
<PipelineUploadRadio
fileContents={fileContents}
onUpload={(data) => setData('fileContents', data)}
setFileContents={(data) => setData('fileContents', data)}
pipelineUrl={pipelineUrl}
setPipelineUrl={(url) => setData('pipelineUrl', url)}
uploadOption={uploadOption}
setUploadOption={(option) => {
setData('uploadOption', option);
}}
/>
</StackItem>
{error && (
Expand Down
Loading

0 comments on commit d127a4b

Please sign in to comment.