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..e3daa8caf6 100644 --- a/frontend/src/__tests__/cypress/cypress/e2e/pipelines/Pipelines.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/e2e/pipelines/Pipelines.cy.ts @@ -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', @@ -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, 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;