From 5151948e698a8bc500d8316fbd41acf5aa6eb5c2 Mon Sep 17 00:00:00 2001 From: Gage Krumbach Date: Thu, 7 Mar 2024 07:59:38 -0600 Subject: [PATCH] Add pipeline url submit modal 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 --- .../cypress/e2e/pipelines/Pipelines.cy.ts | 93 +++++++++++++- .../pages/pipelines/pipelineImportModal.ts | 25 +++- .../pipelines/pipelineVersionImportModal.ts | 29 ++++- frontend/src/api/pipelines/callTypes.ts | 4 + frontend/src/api/pipelines/custom.ts | 11 ++ .../content/import/PipelineImportModal.tsx | 77 +++++++++--- .../content/import/PipelineUploadRadio.tsx | 116 ++++++++++++++++++ .../import/PipelineVersionImportModal.tsx | 75 ++++++++--- .../content/import/useImportModalData.ts | 13 +- .../pipelines/content/import/utils.ts | 8 ++ .../pipelines/context/usePipelineAPIState.ts | 4 + frontend/src/concepts/pipelines/kfTypes.ts | 13 ++ frontend/src/concepts/pipelines/types.ts | 13 ++ 13 files changed, 440 insertions(+), 41 deletions(-) create mode 100644 frontend/src/concepts/pipelines/content/import/PipelineUploadRadio.tsx diff --git a/frontend/src/__tests__/cypress/cypress/e2e/pipelines/Pipelines.cy.ts b/frontend/src/__tests__/cypress/cypress/e2e/pipelines/Pipelines.cy.ts index 6a4798e3ae..1fb6f75b3e 100644 --- a/frontend/src/__tests__/cypress/cypress/e2e/pipelines/Pipelines.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/e2e/pipelines/Pipelines.cy.ts @@ -98,10 +98,56 @@ 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', - description: 'New pipeline version description', pipeline_id: 'test-pipeline', }; @@ -124,7 +170,6 @@ describe('Pipelines', () => { pipelineVersionImportModal.shouldBeOpen(); pipelineVersionImportModal.selectPipelineByName('Test pipeline'); pipelineVersionImportModal.fillVersionName('New pipeline version'); - pipelineVersionImportModal.fillVersionDescription('New pipeline version description'); pipelineVersionImportModal.uploadPipelineYaml(pipelineYamlPath); pipelineVersionImportModal.submit(); @@ -137,6 +182,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, diff --git a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineImportModal.ts b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineImportModal.ts index d7e87b6540..d5b5a262e2 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineImportModal.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineImportModal.ts @@ -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'; @@ -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); } @@ -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) { return cy.intercept( { diff --git a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineVersionImportModal.ts b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineVersionImportModal.ts index 5e120f3caa..8ec3697aa0 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineVersionImportModal.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineVersionImportModal.ts @@ -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'; @@ -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() @@ -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) { return cy.intercept( { diff --git a/frontend/src/api/pipelines/callTypes.ts b/frontend/src/api/pipelines/callTypes.ts index e662fae3eb..15db7f7f0c 100644 --- a/frontend/src/api/pipelines/callTypes.ts +++ b/frontend/src/api/pipelines/callTypes.ts @@ -20,6 +20,8 @@ import { GetPipelineVersion, DeletePipelineVersion, ListPipelineVersions, + CreatePipelineAndVersion, + CreatePipelineVersion, } from '~/concepts/pipelines/types'; import { K8sAPIOptions } from '~/k8sTypes'; @@ -28,6 +30,8 @@ import { K8sAPIOptions } from '~/k8sTypes'; type KubeflowSpecificAPICall = (opts: K8sAPIOptions, ...args: any[]) => Promise; type KubeflowAPICall = (hostPath: string) => ActualCall; +export type CreatePipelineVersionAPI = KubeflowAPICall; +export type CreatePipelineAndVersionAPI = KubeflowAPICall; export type CreateExperimentAPI = KubeflowAPICall; export type CreatePipelineRunAPI = KubeflowAPICall; export type CreatePipelineRunJobAPI = KubeflowAPICall; diff --git a/frontend/src/api/pipelines/custom.ts b/frontend/src/api/pipelines/custom.ts index f580327db9..c6319f0280 100644 --- a/frontend/src/api/pipelines/custom.ts +++ b/frontend/src/api/pipelines/custom.ts @@ -23,6 +23,8 @@ import { GetPipelineVersionAPI, ListPipelineVersionsAPI, UpdatePipelineRunAPI, + CreatePipelineAndVersionAPI, + CreatePipelineVersionAPI, } from './callTypes'; import { handlePipelineFailures } from './errorUtils'; @@ -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)); diff --git a/frontend/src/concepts/pipelines/content/import/PipelineImportModal.tsx b/frontend/src/concepts/pipelines/content/import/PipelineImportModal.tsx index d76482ed84..73c73213f8 100644 --- a/frontend/src/concepts/pipelines/content/import/PipelineImportModal.tsx +++ b/frontend/src/concepts/pipelines/content/import/PipelineImportModal.tsx @@ -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; @@ -24,9 +26,14 @@ const PipelineImportModal: React.FC = ({ isOpen, onClo const { project, api, apiAvailable } = usePipelinesAPI(); const [importing, setImporting] = React.useState(false); const [error, setError] = React.useState(); - 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); @@ -35,6 +42,46 @@ const PipelineImportModal: React.FC = ({ 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 ( = ({ 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 , @@ -88,7 +125,7 @@ const PipelineImportModal: React.FC = ({ isOpen, onClo - = ({ isOpen, onClo - setData('fileContents', data)} + setFileContents={(data) => setData('fileContents', data)} + pipelineUrl={pipelineUrl} + setPipelineUrl={(url) => setData('pipelineUrl', url)} + uploadOption={uploadOption} + setUploadOption={(option) => { + setData('uploadOption', option); + }} /> {error && ( diff --git a/frontend/src/concepts/pipelines/content/import/PipelineUploadRadio.tsx b/frontend/src/concepts/pipelines/content/import/PipelineUploadRadio.tsx new file mode 100644 index 0000000000..9f0f989916 --- /dev/null +++ b/frontend/src/concepts/pipelines/content/import/PipelineUploadRadio.tsx @@ -0,0 +1,116 @@ +import * as React from 'react'; +import { + Alert, + AlertActionLink, + FormGroup, + FormHelperText, + HelperText, + HelperTextItem, + Radio, + Split, + SplitItem, + Stack, + StackItem, + TextInput, +} from '@patternfly/react-core'; +import { ExternalLinkAltIcon } from '@patternfly/react-icons'; +import { COMPILE_PIPELINE_DOCUMENTATION_URL, PipelineUploadOption } from './utils'; +import PipelineFileUpload from './PipelineFileUpload'; + +type PipelineFileUploadProps = { + fileContents: string; + setFileContents: (fileContents: string) => void; + pipelineUrl: string; + setPipelineUrl: (url: string) => void; + uploadOption: PipelineUploadOption; + setUploadOption: (option: PipelineUploadOption) => void; +}; + +const PipelineUploadRadio: React.FC = ({ + fileContents, + setFileContents, + pipelineUrl, + setPipelineUrl, + uploadOption, + setUploadOption, +}) => ( + + + + + { + setUploadOption(PipelineUploadOption.FILE_UPLOAD); + setPipelineUrl(''); + }} + label="Upload a file" + id="upload-file" + data-testid="upload-file-radio" + /> + + + { + setUploadOption(PipelineUploadOption.URL_IMPORT); + setFileContents(''); + }} + label="Import by url" + id="import-url" + data-testid="import-url-radio" + /> + + + + + + + + View documentation + + + + + + + } + /> + + + {uploadOption === PipelineUploadOption.FILE_UPLOAD ? ( + + ) : ( + + setPipelineUrl(value)} + /> + + + URL must be publicly accessible + + + + )} + + +); + +export default PipelineUploadRadio; diff --git a/frontend/src/concepts/pipelines/content/import/PipelineVersionImportModal.tsx b/frontend/src/concepts/pipelines/content/import/PipelineVersionImportModal.tsx index 313f2c9be0..263c30b8f1 100644 --- a/frontend/src/concepts/pipelines/content/import/PipelineVersionImportModal.tsx +++ b/frontend/src/concepts/pipelines/content/import/PipelineVersionImportModal.tsx @@ -7,15 +7,16 @@ import { Modal, Stack, StackItem, + TextArea, TextInput, } from '@patternfly/react-core'; import { usePipelinesAPI } from '~/concepts/pipelines/context'; import { usePipelineVersionImportModalData } from '~/concepts/pipelines/content/import/useImportModalData'; import { getProjectDisplayName } from '~/pages/projects/utils'; -import PipelineFileUpload from '~/concepts/pipelines/content/import/PipelineFileUpload'; import { PipelineKFv2, PipelineVersionKFv2 } from '~/concepts/pipelines/kfTypes'; import PipelineSelector from '~/concepts/pipelines/content/pipelineSelector/PipelineSelector'; -import { generatePipelineVersionName } from './utils'; +import { PipelineUploadOption, generatePipelineVersionName } from './utils'; +import PipelineUploadRadio from './PipelineUploadRadio'; type PipelineVersionImportModalProps = { existingPipeline?: PipelineKFv2 | null; @@ -29,13 +30,21 @@ const PipelineVersionImportModal: React.FC = ({ const { project, api, apiAvailable } = usePipelinesAPI(); const [importing, setImporting] = React.useState(false); const [error, setError] = React.useState(); - const [{ name, description, pipeline, fileContents }, setData, resetData] = - usePipelineVersionImportModalData(existingPipeline); + const [ + { name, description, pipeline, fileContents, uploadOption, pipelineUrl }, + setData, + resetData, + ] = usePipelineVersionImportModalData(existingPipeline); const pipelineId = pipeline?.pipeline_id || ''; const pipelineName = pipeline?.display_name || ''; - const isImportButtonDisabled = !apiAvailable || importing || !name || !fileContents || !pipeline; + const isImportButtonDisabled = + !apiAvailable || + importing || + !name || + !pipeline || + (uploadOption === PipelineUploadOption.URL_IMPORT ? !pipelineUrl : !fileContents); const onBeforeClose = ( pipelineVersion?: PipelineVersionKFv2, @@ -47,6 +56,38 @@ const PipelineVersionImportModal: React.FC = ({ resetData(); }; + const onSubmit = () => { + setImporting(true); + setError(undefined); + + if (uploadOption === PipelineUploadOption.FILE_UPLOAD) { + api + .uploadPipelineVersion({}, name, description, fileContents, pipelineId) + .then((pipelineVersion) => onBeforeClose(pipelineVersion, pipeline)) + .catch((e) => { + setImporting(false); + setError(e); + }); + } else { + api + .createPipelineVersion({}, pipelineId, { + /* eslint-disable camelcase */ + pipeline_id: pipelineId, + display_name: name, + description, + package_url: { + pipeline_url: pipelineUrl, + }, + /* eslint-enable camelcase */ + }) + .then((pipelineVersion) => onBeforeClose(pipelineVersion)) + .catch((e) => { + setImporting(false); + setError(e); + }); + } + }; + return ( = ({ variant="primary" isDisabled={isImportButtonDisabled} isLoading={importing} - onClick={() => { - setImporting(true); - setError(undefined); - api - .uploadPipelineVersion({}, name, description, fileContents, pipelineId) - .then((pipelineVersion) => onBeforeClose(pipelineVersion, pipeline)) - .catch((e) => { - setImporting(false); - setError(e); - }); - }} + onClick={onSubmit} data-testid="upload-version-submit-button" > Upload @@ -110,7 +141,7 @@ const PipelineVersionImportModal: React.FC = ({ - = ({ - setData('fileContents', data)} + setFileContents={(data) => setData('fileContents', data)} + pipelineUrl={pipelineUrl} + setPipelineUrl={(url) => setData('pipelineUrl', url)} + uploadOption={uploadOption} + setUploadOption={(option) => { + setData('uploadOption', option); + }} /> {error && ( diff --git a/frontend/src/concepts/pipelines/content/import/useImportModalData.ts b/frontend/src/concepts/pipelines/content/import/useImportModalData.ts index fe9b89f51e..682bd4f3ff 100644 --- a/frontend/src/concepts/pipelines/content/import/useImportModalData.ts +++ b/frontend/src/concepts/pipelines/content/import/useImportModalData.ts @@ -1,26 +1,35 @@ import React from 'react'; -import { generatePipelineVersionName } from '~/concepts/pipelines/content/import/utils'; +import { + PipelineUploadOption, + generatePipelineVersionName, +} from '~/concepts/pipelines/content/import/utils'; import { PipelineKFv2 } from '~/concepts/pipelines/kfTypes'; import useGenericObjectState, { GenericObjectState } from '~/utilities/useGenericObjectState'; type PipelineModalData = { name: string; description: string; + uploadOption: PipelineUploadOption; fileContents: string; + pipelineUrl: string; }; export const usePipelineImportModalData = (): GenericObjectState => useGenericObjectState({ name: '', description: '', + uploadOption: PipelineUploadOption.FILE_UPLOAD, fileContents: '', + pipelineUrl: '', }); type PipelineVersionModalData = { name: string; description: string; pipeline: PipelineKFv2 | null; + uploadOption: PipelineUploadOption; fileContents: string; + pipelineUrl: string; }; export const usePipelineVersionImportModalData = ( @@ -30,7 +39,9 @@ export const usePipelineVersionImportModalData = ( name: React.useMemo(() => generatePipelineVersionName(existingPipeline), [existingPipeline]), description: '', pipeline: existingPipeline ?? null, + uploadOption: PipelineUploadOption.FILE_UPLOAD, fileContents: '', + pipelineUrl: '', }); return createDataState; diff --git a/frontend/src/concepts/pipelines/content/import/utils.ts b/frontend/src/concepts/pipelines/content/import/utils.ts index c1254bdbc3..42cb8b6f20 100644 --- a/frontend/src/concepts/pipelines/content/import/utils.ts +++ b/frontend/src/concepts/pipelines/content/import/utils.ts @@ -2,3 +2,11 @@ import { PipelineKFv2 } from '~/concepts/pipelines/kfTypes'; export const generatePipelineVersionName = (pipeline?: PipelineKFv2 | null): string => pipeline ? `${pipeline.display_name}_version_at_${new Date().toISOString()}` : ''; + +export const COMPILE_PIPELINE_DOCUMENTATION_URL = + 'https://www.kubeflow.org/docs/components/pipelines/v2/compile-a-pipeline/'; + +export enum PipelineUploadOption { + URL_IMPORT, + FILE_UPLOAD, +} diff --git a/frontend/src/concepts/pipelines/context/usePipelineAPIState.ts b/frontend/src/concepts/pipelines/context/usePipelineAPIState.ts index 598a737fb9..faa2b311a2 100644 --- a/frontend/src/concepts/pipelines/context/usePipelineAPIState.ts +++ b/frontend/src/concepts/pipelines/context/usePipelineAPIState.ts @@ -25,6 +25,8 @@ import { uploadPipelineVersion, archivePipelineRun, unarchivePipelineRun, + createPipelineAndVersion, + createPipelineVersion, } from '~/api'; import { PipelineAPIs } from '~/concepts/pipelines/types'; import { APIState } from '~/concepts/proxy/types'; @@ -37,6 +39,8 @@ const usePipelineAPIState = ( ): [apiState: PipelineAPIState, refreshAPIState: () => void] => { const createAPI = React.useCallback( (path: string) => ({ + createPipelineVersion: createPipelineVersion(path), + createPipelineAndVersion: createPipelineAndVersion(path), createExperiment: createExperiment(path), createPipelineRun: createPipelineRun(path), createPipelineRunJob: createPipelineRunJob(path), diff --git a/frontend/src/concepts/pipelines/kfTypes.ts b/frontend/src/concepts/pipelines/kfTypes.ts index f3ec9066b4..7d1cc4535b 100644 --- a/frontend/src/concepts/pipelines/kfTypes.ts +++ b/frontend/src/concepts/pipelines/kfTypes.ts @@ -472,6 +472,19 @@ export type ListPipelineVersionsKF = PipelineKFCallCommon<{ pipeline_versions: PipelineVersionKFv2[]; }>; +export type CreatePipelineAndVersionKFData = { + pipeline: Omit; + pipeline_version: Omit< + PipelineVersionKFv2, + 'pipeline_id' | 'pipeline_version_id' | 'error' | 'created_at' | 'pipeline_spec' + >; +}; + +export type CreatePipelineVersionKFData = Omit< + PipelineVersionKFv2, + 'pipeline_version_id' | 'error' | 'created_at' | 'pipeline_spec' +>; + export type CreateExperimentKFData = Omit< ExperimentKFv2, 'experiment_id' | 'created_at' | 'namespace' | 'storage_state' diff --git a/frontend/src/concepts/pipelines/types.ts b/frontend/src/concepts/pipelines/types.ts index ef7e76b127..3a4757a1da 100644 --- a/frontend/src/concepts/pipelines/types.ts +++ b/frontend/src/concepts/pipelines/types.ts @@ -14,6 +14,8 @@ import { PipelineCoreResourceKFv2, PipelineRunKFv2, PipelineRunJobKFv2, + CreatePipelineAndVersionKFData, + CreatePipelineVersionKFData, CreateExperimentKFData, } from './kfTypes'; @@ -41,6 +43,15 @@ export type PipelineListPaged = { items: T[]; }; +export type CreatePipelineVersion = ( + opts: K8sAPIOptions, + pipelineId: string, + data: CreatePipelineVersionKFData, +) => Promise; +export type CreatePipelineAndVersion = ( + opts: K8sAPIOptions, + data: CreatePipelineAndVersionKFData, +) => Promise; export type CreateExperiment = ( opts: K8sAPIOptions, data: CreateExperimentKFData, @@ -118,6 +129,8 @@ export type UploadPipelineVersion = ( ) => Promise; export type PipelineAPIs = { + createPipelineVersion: CreatePipelineVersion; + createPipelineAndVersion: CreatePipelineAndVersion; createExperiment: CreateExperiment; createPipelineRun: CreatePipelineRun; createPipelineRunJob: CreatePipelineRunJob;