From 4509317418d3feb3d7c70a1c54b13c2cbef1b40c Mon Sep 17 00:00:00 2001 From: "Yuan (Bob) Gong" Date: Thu, 13 Feb 2020 16:06:26 +0800 Subject: [PATCH] [UI] Upload pipeline version from local package (#3059) * [UI] Upload pipeline version from local package * Fix test error * Improve broken license file --- .../visualization/third_party_licenses.csv | 2 +- frontend/src/lib/Apis.ts | 21 +- frontend/src/lib/Buttons.ts | 2 +- frontend/src/pages/NewPipelineVersion.tsx | 266 +++++++++--------- frontend/src/pages/PipelineDetails.tsx | 2 +- .../NewPipelineVersion.test.tsx.snap | 2 +- 6 files changed, 153 insertions(+), 142 deletions(-) diff --git a/backend/src/apiserver/visualization/third_party_licenses.csv b/backend/src/apiserver/visualization/third_party_licenses.csv index 1b26577d79e0..1eeaa62a2182 100644 --- a/backend/src/apiserver/visualization/third_party_licenses.csv +++ b/backend/src/apiserver/visualization/third_party_licenses.csv @@ -78,7 +78,7 @@ packaging,https://creativecommons.org/licenses/by-sa/3.0/,CC by-SA 3 pandas,https://github.com/pandas-dev/pandas/blob/master/LICENSE,BSD-3 pandocfilters,https://github.com/jgm/pandocfilters/blob/master/LICENSE,BSD-3 parso,https://github.com/davidhalter/parso/blob/master/LICENSE.txt,MIT -pbr,https://opendev.org/openstack/pbr/src/branch/master/LICENSE,Apache 2.0 +pbr,https://opendev.org/openstack/pbr/raw/branch/master/LICENSE,Apache 2.0 pexpect,https://github.com/pexpect/pexpect/blob/master/LICENSE,ISC pickleshare,https://github.com/pickleshare/pickleshare/blob/master/LICENSE,MIT prometheus-client,https://github.com/prometheus/client_python/blob/master/LICENSE,Apache 2.0 diff --git a/frontend/src/lib/Apis.ts b/frontend/src/lib/Apis.ts index c3c75bd377ea..c37713b9d63d 100644 --- a/frontend/src/lib/Apis.ts +++ b/frontend/src/lib/Apis.ts @@ -16,7 +16,7 @@ import * as portableFetch from 'portable-fetch'; import { HTMLViewerConfig } from 'src/components/viewers/HTMLViewer'; import { ExperimentServiceApi, FetchAPI } from '../apis/experiment'; import { JobServiceApi } from '../apis/job'; -import { ApiPipeline, PipelineServiceApi } from '../apis/pipeline'; +import { ApiPipeline, PipelineServiceApi, ApiPipelineVersion } from '../apis/pipeline'; import { RunServiceApi } from '../apis/run'; import { ApiVisualization, VisualizationServiceApi } from '../apis/visualization'; import { PlotType } from '../components/viewers/Viewer'; @@ -241,6 +241,25 @@ export class Apis { ); } + public static async uploadPipelineVersion( + versionName: string, + pipelineId: string, + versionData: File, + ): Promise { + const fd = new FormData(); + fd.append('uploadfile', versionData, versionData.name); + return await this._fetchAndParse( + '/pipelines/upload_version', + v1beta1Prefix, + `name=${encodeURIComponent(versionName)}&pipelineid=${encodeURIComponent(pipelineId)}`, + { + body: fd, + cache: 'no-cache', + method: 'POST', + }, + ); + } + /* * Retrieves the name of the Kubernetes cluster if it is running in GKE, otherwise returns an error. */ diff --git a/frontend/src/lib/Buttons.ts b/frontend/src/lib/Buttons.ts index daddf2439a95..f05d4026ea91 100644 --- a/frontend/src/lib/Buttons.ts +++ b/frontend/src/lib/Buttons.ts @@ -296,7 +296,7 @@ export default class Buttons { outlined: true, style: { minWidth: 160 }, title: label, - tooltip: 'Upload pipeline or pipeline version', + tooltip: 'Upload pipeline version', }; return this; } diff --git a/frontend/src/pages/NewPipelineVersion.tsx b/frontend/src/pages/NewPipelineVersion.tsx index 5d44045741e0..aa8058aa7b4a 100644 --- a/frontend/src/pages/NewPipelineVersion.tsx +++ b/frontend/src/pages/NewPipelineVersion.tsx @@ -210,9 +210,9 @@ class NewPipelineVersion extends Page<{}, NewPipelineVersionState> { /> - {/* Form for uploading new pipeline */} + {/* Pipeline name and help text for uploading new pipeline */} {newPipeline === true && ( - + <>
Upload pipeline with the specified package.
{ /> {/* Choose a local file for package or specify a url for package */} - - {/* Different package explanation based on import method*/} - {this.state.importMethod === ImportMethod.LOCAL && ( - -
- Choose a pipeline package file from your computer, and give the pipeline a - unique name. -
- You can also drag and drop the file here. -
- -
- )} - {this.state.importMethod === ImportMethod.URL && ( - -
URL must be publicly accessible.
- -
- )} - - {/* Different package input field based on import method*/} -
- } - onChange={() => this.setState({ importMethod: ImportMethod.LOCAL })} - /> - - {dropzoneActive &&
Drop files..
} - - - - ), - readOnly: true, - style: { - maxWidth: 2000, - width: 455, - }, - }} - /> -
-
-
- } - onChange={() => this.setState({ importMethod: ImportMethod.URL })} - /> - -
- {/* Fill pipeline version code source url */} - -
+ )} - {/* Form for uploading new pipeline version */} + {/* Pipeline selector and help text for uploading new pipeline version */} {newPipeline === false && ( - + <>
Upload pipeline version with the specified package.
@@ -435,28 +332,112 @@ class NewPipelineVersion extends Page<{}, NewPipelineVersionState> { autoFocus={true} variant='outlined' /> + + )} - {/* Fill pipeline package url */} - + {/* Different package explanation based on import method*/} + {this.state.importMethod === ImportMethod.LOCAL && ( + <> +
+ Choose a pipeline package file from your computer, and give the pipeline a unique + name. +
+ You can also drag and drop the file here. +
+ + + )} + {this.state.importMethod === ImportMethod.URL && ( + <> +
URL must be publicly accessible.
+ + + )} - {/* Fill pipeline version code source url */} + {/* Different package input field based on import method*/} +
+ } + onChange={() => this.setState({ importMethod: ImportMethod.LOCAL })} + /> + + {dropzoneActive &&
Drop files..
} + + + ), + readOnly: true, + style: { + maxWidth: 2000, + width: 455, + }, + }} /> - - )} +
+
+
+ } + onChange={() => this.setState({ importMethod: ImportMethod.URL })} + /> + +
+ + {/* Fill pipeline version code source url */} + {/* Create pipeline or pipeline version */}
@@ -604,15 +585,26 @@ class NewPipelineVersion extends Page<{}, NewPipelineVersionState> { } }; - const newPipelineVersion: ApiPipelineVersion = { - code_source_url: this.state.codeSourceUrl, - name: this.state.pipelineVersionName, - package_url: { pipeline_url: this.state.packageUrl }, - resource_references: [ - { key: { id: await getPipelineId(), type: ApiResourceType.PIPELINE }, relationship: 1 }, - ], - }; - return Apis.pipelineServiceApi.createPipelineVersion(newPipelineVersion); + if (this.state.importMethod === ImportMethod.LOCAL) { + if (!this.state.file) { + throw new Error('File should be selected'); + } + return Apis.uploadPipelineVersion( + this.state.pipelineVersionName, + await getPipelineId(), + this.state.file, + ); + } else { + // this.state.importMethod === ImportMethod.URL + return Apis.pipelineServiceApi.createPipelineVersion({ + code_source_url: this.state.codeSourceUrl, + name: this.state.pipelineVersionName, + package_url: { pipeline_url: this.state.packageUrl }, + resource_references: [ + { key: { id: await getPipelineId(), type: ApiResourceType.PIPELINE }, relationship: 1 }, + ], + }); + } } private _validate(): void { @@ -625,7 +617,7 @@ class NewPipelineVersion extends Page<{}, NewPipelineVersionState> { try { if (newPipeline) { if (!packageUrl && !fileName) { - throw new Error('Must specify either package url or file'); + throw new Error('Must specify either package url or file in .yaml, .zip, or .tar.gz'); } } else { if (!pipeline) { @@ -634,8 +626,8 @@ class NewPipelineVersion extends Page<{}, NewPipelineVersionState> { if (!pipelineVersionName) { throw new Error('Pipeline version name is required'); } - if (!packageUrl) { - throw new Error('Please specify a pipeline package in .yaml, .zip, or .tar.gz'); + if (!packageUrl && !fileName) { + throw new Error('Please specify either package url or file in .yaml, .zip, or .tar.gz'); } } this.setState({ validationError: '' }); diff --git a/frontend/src/pages/PipelineDetails.tsx b/frontend/src/pages/PipelineDetails.tsx index 53d28158e6ce..e664ceeedec0 100644 --- a/frontend/src/pages/PipelineDetails.tsx +++ b/frontend/src/pages/PipelineDetails.tsx @@ -137,7 +137,7 @@ class PipelineDetails extends Page<{}, PipelineDetailsState> { return pipelineVersionIdFromParams ? pipelineVersionIdFromParams : ''; }, ) - .newPipelineVersion('Upload pipeline version', () => + .newPipelineVersion('Upload version', () => pipelineIdFromParams ? pipelineIdFromParams : '', ); diff --git a/frontend/src/pages/__snapshots__/NewPipelineVersion.test.tsx.snap b/frontend/src/pages/__snapshots__/NewPipelineVersion.test.tsx.snap index 3d3248c49dc0..e676cb24726b 100644 --- a/frontend/src/pages/__snapshots__/NewPipelineVersion.test.tsx.snap +++ b/frontend/src/pages/__snapshots__/NewPipelineVersion.test.tsx.snap @@ -205,7 +205,7 @@ exports[`NewPipelineVersion creating new pipeline renders the new pipeline page
- Must specify either package url or file + Must specify either package url or file in .yaml, .zip, or .tar.gz