From 9fcc9e310bbce4bb41a0942551513e6263459c46 Mon Sep 17 00:00:00 2001
From: Paulo Rego <832830+paulovmr@users.noreply.github.com>
Date: Mon, 16 Dec 2024 14:53:30 -0300
Subject: [PATCH] Migrate Elyra extensions to support JupyterLab 4.2.5 (#2)
---------
Signed-off-by: Harshad Reddy Nalla
Signed-off-by: shalberd <21118431+shalberd@users.noreply.github.com>
Co-authored-by: Guilherme Caponetto <638737+caponetto@users.noreply.github.com>
Co-authored-by: Eder Ignatowicz
Co-authored-by: Edson Tirelli
Co-authored-by: Harshad Reddy Nalla
Co-authored-by: Sven Thoms <21118431+shalberd@users.noreply.github.com>
---
.eslintrc.json | 14 +-
.github/workflows/build.yml | 129 +-
.gitignore | 3 +
.prettierrc | 3 +-
.yarnrc.yml | 2 -
Makefile | 27 +-
build_requirements.txt | 2 +-
docs/requirements.txt | 2 +-
elyra/metadata/schemas/airflow.json | 8 +-
elyra/metadata/schemas/code-snippet.json | 6 +-
elyra/metadata/schemas/kfp.json | 8 +-
elyra/metadata/schemas/metadata-test.json | 4 +-
elyra/metadata/schemas/metadata-test2.json | 4 +-
elyra/metadata/schemas/runtime-image.json | 4 +-
elyra/metadata/schemas/url-catalog.json | 2 +-
.../airflow-package-catalog.json | 2 +-
.../airflow-provider-package-catalog.json | 2 +-
elyra/pipeline/kfp/PipelineConf.py | 158 +
elyra/pipeline/kfp/kfp_authentication.py | 8 +-
elyra/pipeline/kfp/processor_kfp.py | 97 +-
elyra/pipeline/pipeline_definition.py | 3 +
...neric_component_definition_template.jinja2 | 24 +
.../kubeflow/v2/python_dsl_template.jinja2 | 152 +
.../pipelines/kf_inputpath_parameter.pipeline | 6 +-
elyra/tests/cli/test_pipeline_app.py | 9 +
elyra/tests/metadata/test_metadata.py | 2 +-
.../tests/pipeline/kfp/test_processor_kfp.py | 490 +-
.../resources/components/download_data.yaml | 4 +-
.../resources/components/filter_text.yaml | 16 +-
.../kf_inputpath_parameter.pipeline | 6 +-
...alid_inputpath_missing_connection.pipeline | 4 +-
.../kf_invalid_inputpath_parameter.pipeline | 6 +-
...kf_supernode_invalid_single_cycle.pipeline | 4 +-
.../kf_supernode_valid.pipeline | 4 +-
.../kf_with_parameters.pipeline | 2 +-
etc/docker/elyra_development/requirements.yml | 4 +-
etc/generic/requirements-elyra.txt | 26 +-
etc/scripts/generate-make-graph.ts | 8 +-
.../elyra_code_snippet_extension/__init__.py | 14 +
.../elyra_metadata_common/__init__.py | 14 +
.../elyra_metadata_extension/__init__.py | 14 +
.../__init__.py | 14 +
.../elyra_python_editor_extension/__init__.py | 14 +
.../__init__.py | 14 +
labextensions/elyra_script_editor/__init__.py | 14 +
labextensions/elyra_services/__init__.py | 14 +
.../elyra_theme_extension/__init__.py | 14 +
labextensions/elyra_ui_components/__init__.py | 14 +
package.json | 15 +-
packages/code-snippet/install.json | 5 +
packages/code-snippet/package.json | 62 +-
packages/code-snippet/setup.py | 1 +
.../code-snippet/src/CodeSnippetService.ts | 4 +-
.../code-snippet/src/CodeSnippetWidget.tsx | 127 +-
packages/code-snippet/src/index.ts | 47 +-
packages/code-viewer/package.json | 2 +-
packages/code-viewer/src/CodeViewerWidget.ts | 4 +-
packages/code-viewer/src/index.ts | 20 +-
packages/metadata-common/install.json | 5 +
packages/metadata-common/package.json | 43 +-
packages/metadata-common/setup.py | 1 +
packages/metadata-common/src/FilterTools.tsx | 30 +-
.../src/MetadataCommonService.tsx | 6 +-
.../metadata-common/src/MetadataEditor.tsx | 8 +-
.../src/MetadataEditorWidget.tsx | 22 +-
.../metadata-common/src/MetadataWidget.tsx | 56 +-
packages/metadata/install.json | 5 +
packages/metadata/package.json | 47 +-
packages/metadata/setup.py | 1 +
packages/metadata/src/index.ts | 76 +-
packages/pipeline-editor/install.json | 5 +
packages/pipeline-editor/package.json | 72 +-
.../schema/src/ComponentCatalogsWidget.tsx | 222 -
.../schema/src/EmptyPipelineContent.tsx | 121 -
.../schema/src/FileSubmissionDialog.tsx | 144 -
.../schema/src/ParameterInputForm.tsx | 124 -
.../schema/src/PipelineEditorWidget.tsx | 1215 --
.../schema/src/PipelineExportDialog.tsx | 98 -
.../schema/src/PipelineService.tsx | 359 -
.../schema/src/PipelineSubmissionDialog.tsx | 56 -
.../schema/src/RuntimeConfigSelect.tsx | 99 -
.../schema/src/RuntimeImagesWidget.tsx | 112 -
.../schema/src/RuntimesWidget.tsx | 239 -
.../schema/src/SubmitFileButtonExtension.tsx | 174 -
.../pipeline-editor/schema/src/dialogs.tsx | 68 -
.../schema/src/formDialogWidget.ts | 49 -
packages/pipeline-editor/schema/src/index.ts | 395 -
.../schema/src/pipeline-hooks.ts | 297 -
.../schema/src/runtime-utils.ts | 100 -
.../schema/src/test/pipeline-hooks.spec.ts | 183 -
.../schema/src/test/pipeline-service.spec.ts | 48 -
packages/pipeline-editor/schema/src/theme.tsx | 121 -
packages/pipeline-editor/schema/src/utils.ts | 125 -
packages/pipeline-editor/setup.py | 1 +
.../src/ComponentCatalogsWidget.tsx | 20 +-
.../src/EmptyPipelineContent.tsx | 2 +-
.../src/FileSubmissionDialog.tsx | 2 +-
.../src/ParameterInputForm.tsx | 8 +-
.../src/PipelineEditorWidget.tsx | 280 +-
.../src/PipelineExportDialog.tsx | 2 +-
.../pipeline-editor/src/PipelineService.tsx | 48 +-
.../src/PipelineSubmissionDialog.tsx | 2 +-
.../src/RuntimeConfigSelect.tsx | 6 +-
.../src/RuntimeImagesWidget.tsx | 2 +-
.../pipeline-editor/src/RuntimesWidget.tsx | 16 +-
.../src/SubmitFileButtonExtension.tsx | 30 +-
packages/pipeline-editor/src/dialogs.tsx | 12 +-
.../pipeline-editor/src/formDialogWidget.ts | 4 +-
packages/pipeline-editor/src/index.ts | 421 +-
.../pipeline-editor/src/pipeline-hooks.ts | 88 +-
packages/pipeline-editor/src/runtime-utils.ts | 16 +-
.../src/test/pipeline-hooks.spec.ts | 58 +-
.../src/test/pipeline-service.spec.ts | 4 +-
packages/pipeline-editor/src/theme.tsx | 28 +-
packages/pipeline-editor/src/utils.ts | 28 +-
packages/python-editor/install.json | 5 +
packages/python-editor/package.json | 59 +-
packages/python-editor/setup.py | 1 +
packages/python-editor/src/PythonEditor.tsx | 2 +-
packages/python-editor/src/index.ts | 62 +-
packages/r-editor/package.json | 2 +-
packages/r-editor/src/REditor.tsx | 2 +-
packages/r-editor/src/index.ts | 44 +-
packages/scala-editor/package.json | 2 +-
packages/scala-editor/src/ScalaEditor.tsx | 2 +-
packages/scala-editor/src/index.ts | 44 +-
packages/script-debugger/install.json | 5 +
packages/script-debugger/package.json | 45 +-
packages/script-debugger/setup.py | 1 +
packages/script-debugger/src/index.ts | 20 +-
packages/script-editor/install.json | 5 +
packages/script-editor/package.json | 61 +-
packages/script-editor/setup.py | 1 +
packages/script-editor/src/KernelDropdown.tsx | 19 +-
packages/script-editor/src/ScriptEditor.tsx | 60 +-
.../src/ScriptEditorController.ts | 4 +-
.../src/ScriptEditorWidgetFactory.tsx | 8 +-
packages/script-editor/src/ScriptRunner.ts | 55 +-
.../src/test/script-editor.spec.ts | 4 +-
packages/script-editor/style/index.css | 18 -
packages/services/install.json | 5 +
packages/services/jest.config.js | 2 +-
packages/services/package.json | 49 +-
packages/services/setup.py | 1 +
packages/services/src/metadata.ts | 16 +-
packages/services/src/parsing.ts | 2 +-
packages/services/src/requests.ts | 22 +-
packages/services/src/test/services.spec.ts | 34 +-
packages/theme/install.json | 5 +
packages/theme/package.json | 49 +-
packages/theme/setup.py | 1 +
packages/theme/src/index.ts | 35 +-
packages/theme/src/launcher.tsx | 10 +-
packages/ui-components/install.json | 5 +
packages/ui-components/package.json | 44 +-
packages/ui-components/setup.py | 1 +
.../ui-components/src/BrowseFileDialog.tsx | 30 +-
packages/ui-components/src/Dropzone.tsx | 6 +-
.../ui-components/src/ExpandableComponent.tsx | 4 +-
.../src/ExpandableErrorDialog.tsx | 6 +-
.../src/FormComponents/CodeBlock.tsx | 56 +-
.../src/FormComponents/DropDown.tsx | 41 +-
.../src/FormComponents/PasswordField.tsx | 23 +-
.../ui-components/src/FormComponents/Tags.tsx | 48 +-
packages/ui-components/src/FormDialog.tsx | 12 +-
packages/ui-components/src/FormEditor.tsx | 49 +-
packages/ui-components/src/JSONComponent.tsx | 6 +-
packages/ui-components/src/RequestErrors.tsx | 8 +-
packages/ui-components/src/icons.tsx | 48 +-
pyproject.toml | 46 +-
scripts/start-test-server.ts | 2 +-
tests/.eslintrc.js | 6 +-
tests/integration/01-scriptdebugger.ts | 25 +-
tests/integration/codesnippet.ts | 36 +-
.../codesnippetfromselectedcells.ts | 72 +-
tests/integration/launcher.ts | 14 +-
tests/integration/lsp.ts | 2 +-
tests/integration/pipeline.ts | 130 +-
tests/integration/pythoneditor.ts | 48 +-
tests/integration/reditor.ts | 8 +-
tests/integration/submitnotebookbutton.ts | 18 +-
.../matches-complex-pipeline-snapshot.1.snap | 498 +-
.../matches-empty-pipeline-snapshot.1.snap | 36 +-
.../matches-simple-pipeline-snapshot.1.snap | 158 +-
tests/support/commands.ts | 103 +-
tests/tsconfig.json | 8 +-
tests/utils/snapshots/plugin.ts | 16 +-
testutils/babel.config.js | 17 -
testutils/jest.config.js | 57 +-
testutils/jest.setup.js | 5 +-
testutils/transform.js | 18 -
yarn.lock | 11713 +++++++++-------
192 files changed, 9683 insertions(+), 11988 deletions(-)
create mode 100644 elyra/pipeline/kfp/PipelineConf.py
create mode 100644 elyra/templates/kubeflow/v2/generic_component_definition_template.jinja2
create mode 100644 elyra/templates/kubeflow/v2/python_dsl_template.jinja2
create mode 100644 labextensions/elyra_code_snippet_extension/__init__.py
create mode 100644 labextensions/elyra_metadata_common/__init__.py
create mode 100644 labextensions/elyra_metadata_extension/__init__.py
create mode 100644 labextensions/elyra_pipeline_editor_extension/__init__.py
create mode 100644 labextensions/elyra_python_editor_extension/__init__.py
create mode 100644 labextensions/elyra_script_debugger_extension/__init__.py
create mode 100644 labextensions/elyra_script_editor/__init__.py
create mode 100644 labextensions/elyra_services/__init__.py
create mode 100644 labextensions/elyra_theme_extension/__init__.py
create mode 100644 labextensions/elyra_ui_components/__init__.py
create mode 100644 packages/code-snippet/install.json
create mode 100644 packages/code-snippet/setup.py
create mode 100644 packages/metadata-common/install.json
create mode 100644 packages/metadata-common/setup.py
create mode 100644 packages/metadata/install.json
create mode 100644 packages/metadata/setup.py
create mode 100644 packages/pipeline-editor/install.json
delete mode 100644 packages/pipeline-editor/schema/src/ComponentCatalogsWidget.tsx
delete mode 100644 packages/pipeline-editor/schema/src/EmptyPipelineContent.tsx
delete mode 100644 packages/pipeline-editor/schema/src/FileSubmissionDialog.tsx
delete mode 100644 packages/pipeline-editor/schema/src/ParameterInputForm.tsx
delete mode 100644 packages/pipeline-editor/schema/src/PipelineEditorWidget.tsx
delete mode 100644 packages/pipeline-editor/schema/src/PipelineExportDialog.tsx
delete mode 100644 packages/pipeline-editor/schema/src/PipelineService.tsx
delete mode 100644 packages/pipeline-editor/schema/src/PipelineSubmissionDialog.tsx
delete mode 100644 packages/pipeline-editor/schema/src/RuntimeConfigSelect.tsx
delete mode 100644 packages/pipeline-editor/schema/src/RuntimeImagesWidget.tsx
delete mode 100644 packages/pipeline-editor/schema/src/RuntimesWidget.tsx
delete mode 100644 packages/pipeline-editor/schema/src/SubmitFileButtonExtension.tsx
delete mode 100644 packages/pipeline-editor/schema/src/dialogs.tsx
delete mode 100644 packages/pipeline-editor/schema/src/formDialogWidget.ts
delete mode 100644 packages/pipeline-editor/schema/src/index.ts
delete mode 100644 packages/pipeline-editor/schema/src/pipeline-hooks.ts
delete mode 100644 packages/pipeline-editor/schema/src/runtime-utils.ts
delete mode 100644 packages/pipeline-editor/schema/src/test/pipeline-hooks.spec.ts
delete mode 100644 packages/pipeline-editor/schema/src/test/pipeline-service.spec.ts
delete mode 100644 packages/pipeline-editor/schema/src/theme.tsx
delete mode 100644 packages/pipeline-editor/schema/src/utils.ts
create mode 100644 packages/pipeline-editor/setup.py
create mode 100644 packages/python-editor/install.json
create mode 100644 packages/python-editor/setup.py
create mode 100644 packages/script-debugger/install.json
create mode 100644 packages/script-debugger/setup.py
create mode 100644 packages/script-editor/install.json
create mode 100644 packages/script-editor/setup.py
create mode 100644 packages/services/install.json
create mode 100644 packages/services/setup.py
create mode 100644 packages/theme/install.json
create mode 100644 packages/theme/setup.py
create mode 100644 packages/ui-components/install.json
create mode 100644 packages/ui-components/setup.py
delete mode 100644 testutils/babel.config.js
delete mode 100644 testutils/transform.js
diff --git a/.eslintrc.json b/.eslintrc.json
index d6f5bf646..42c8f3e3e 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -73,5 +73,17 @@
"react": {
"version": "detect"
}
- }
+ },
+ "ignorePatterns": [
+ "packages/pipeline-editor/elyra_pipeline_editor_extension/*",
+ "packages/theme/elyra_theme_extension/*",
+ "packages/script-debugger/elyra_script_debugger_extension/*",
+ "packages/script-editor/elyra_script_editor/*",
+ "packages/code-snippet/elyra_code_snippet_extension/*",
+ "packages/metadata/elyra_metadata_extension/*",
+ "packages/metadata-common/elyra_metadata_common/*",
+ "packages/python-editor/elyra_python_editor_extension/*",
+ "packages/services/elyra_services/*",
+ "packages/ui-components/elyra_ui_components/*"
+ ]
}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 591651a10..d0277b98f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -19,38 +19,43 @@ on:
pull_request: # all branches
schedule:
# once a day at 3 am (PST) (10 am (UTC))
- - cron: '0 10 * * *'
+ - cron: '0 10 * * *'
env:
FORCE_COLOR: true
+ NODE_VERSION: 20.11.0
+ YARN_VERSION: 3.5.0
+ PYTHON_VERSION: 3.11
jobs:
prepare-yarn-cache:
name: Prepare Cache
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-node@v3
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
with:
- node-version: "20.11.0"
- - run: npm install -g yarn@3.5.0
- - uses: actions/cache@v3
+ node-version: ${{ env.NODE_VERSION }}
+ - uses: actions/cache@v4
with:
path: |
node_modules
*/*/node_modules
/home/runner/.cache/Cypress
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
- - name: Install
+ - name: Install Yarn ${{ env.YARN_VERSION }}
run: |
+ corepack prepare yarn@${{ env.YARN_VERSION }} --activate
+ yarn set version ${{ env.YARN_VERSION }}
yarn --version
- yarn install && tsc -v
- # --frozen-lockfile
+ - name: Install dependencies
+ run: |
+ yarn install
lint-server:
name: Lint Server
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Lint
run: make lint-server
@@ -59,18 +64,24 @@ jobs:
needs: prepare-yarn-cache
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-node@v3
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
with:
- node-version: "*"
- - uses: actions/cache@v3
+ node-version: ${{ env.NODE_VERSION }}
+ - uses: actions/cache@v4
with:
path: |
node_modules
*/*/node_modules
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
- - name: Install
- run: make yarn-install
+ - name: Install Yarn ${{ env.YARN_VERSION }}
+ run: |
+ corepack prepare yarn@${{ env.YARN_VERSION }} --activate
+ yarn set version ${{ env.YARN_VERSION }}
+ yarn --version
+ - name: Install dependencies
+ run: |
+ make yarn-install
- name: Lint
run: make eslint-check-ui
- name: Check format
@@ -81,10 +92,10 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: [3.8, 3.9, "3.10"]
+ python-version: [3.8, 3.9, '3.10', '3.11']
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-python@v4
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install
@@ -110,19 +121,24 @@ jobs:
needs: prepare-yarn-cache
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-node@v3
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
with:
- node-version: "*"
- - uses: actions/setup-python@v4
+ node-version: ${{ env.NODE_VERSION }}
+ - uses: actions/setup-python@v5
with:
- python-version: "3.10"
- - uses: actions/cache@v3
+ python-version: ${{ env.PYTHON_VERSION }}
+ - uses: actions/cache@v4
with:
path: |
node_modules
*/*/node_modules
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
+ - name: Install Yarn ${{ env.YARN_VERSION }}
+ run: |
+ corepack prepare yarn@${{ env.YARN_VERSION }} --activate
+ yarn set version ${{ env.YARN_VERSION }}
+ yarn --version
- name: Build
run: |
make build-dependencies
@@ -138,20 +154,25 @@ jobs:
needs: prepare-yarn-cache
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-node@v3
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
with:
- node-version: "*"
- - uses: actions/setup-python@v4
+ node-version: ${{ env.NODE_VERSION }}
+ - uses: actions/setup-python@v5
with:
- python-version: "3.10"
- - uses: actions/cache@v3
+ python-version: ${{ env.PYTHON_VERSION }}
+ - uses: actions/cache@v4
with:
path: |
node_modules
*/*/node_modules
/home/runner/.cache/Cypress
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
+ - name: Install Yarn ${{ env.YARN_VERSION }}
+ run: |
+ corepack prepare yarn@${{ env.YARN_VERSION }} --activate
+ yarn set version ${{ env.YARN_VERSION }}
+ yarn --version
- name: Build
run: |
make build-dependencies
@@ -164,9 +185,10 @@ jobs:
- name: Cypress
run: make test-integration
- name: Collect logs
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
if: failure()
with:
+ name: elyra_test_artifacts
path: |
${{ github.workspace }}/build/cypress-tests/*.log
${{ github.workspace }}/build/cypress-tests/screenshots//**/*
@@ -177,7 +199,7 @@ jobs:
name: Test documentation build
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Build
run: make docs
@@ -186,17 +208,50 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: [3.8, 3.9, "3.10"]
+ python-version: [3.8, 3.9, '3.10', '3.11']
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Validate image environment
run: make PYTHON_VERSION=${{ matrix.python-version }} elyra-image-env
-
validate-images:
name: Validate Images
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Validate runtime images
run: make REMOVE_RUNTIME_IMAGE=1 validate-runtime-images
+
+ upload-artifacts:
+ name: Upload Artifacts
+ needs: prepare-yarn-cache
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: ${{ env.NODE_VERSION }}
+ - uses: actions/setup-python@v5
+ with:
+ python-version: ${{ env.PYTHON_VERSION }}
+ - uses: actions/cache@v4
+ with:
+ path: |
+ node_modules
+ */*/node_modules
+ /home/runner/.cache/Cypress
+ key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
+ - name: Install Yarn ${{ env.YARN_VERSION }}
+ run: |
+ corepack prepare yarn@${{ env.YARN_VERSION }} --activate
+ yarn set version ${{ env.YARN_VERSION }}
+ yarn --version
+ - name: Build
+ run: |
+ make clean install
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: elyra_build_artifacts
+ path: |
+ ${{ github.workspace }}/dist
diff --git a/.gitignore b/.gitignore
index 6a73ca5e2..73f87233d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -62,6 +62,7 @@ htmlcov/
nosetests.xml
coverage.xml
*,cover
+**/coverage/
# Translations
*.mo
@@ -92,3 +93,5 @@ node_modules
.vscode/
.yarn/
+
+labextensions/**/labextension/*
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
index 544138be4..4c112c074 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,3 +1,4 @@
{
- "singleQuote": true
+ "singleQuote": true,
+ "trailingComma" : "none"
}
diff --git a/.yarnrc.yml b/.yarnrc.yml
index 436b801f4..fe1125f54 100644
--- a/.yarnrc.yml
+++ b/.yarnrc.yml
@@ -1,5 +1,3 @@
enableImmutableInstalls: false
nodeLinker: node-modules
-
-yarnPath: .yarn/releases/yarn-3.5.0.cjs
diff --git a/Makefile b/Makefile
index 9a68fd52c..38a86f0bc 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@
# limitations under the License.
#
-.PHONY: help purge uninstall-src uninstall clean
+.PHONY: help purge uninstall clean
.PHONY: lint-dependencies lint-server black-format prettier-check-ui eslint-check-ui prettier-ui eslint-ui lint-ui lint
.PHONY: dev-link dev-unlink
.PHONY: build-dependencies dev-dependencies yarn-install build-ui package-ui package-ui-dev
@@ -35,7 +35,7 @@ CONTAINER_OUTPUT_OPTION?=--output=type=docker
# Python execs
PYTHON?=python3
PYTHON_PIP=$(PYTHON) -m pip
-PYTHON_VERSION?=3.9
+PYTHON_VERSION?=3.11
CONDA_ACTIVATE = source $$(conda info --base)/etc/profile.d/conda.sh ; conda activate
@@ -75,24 +75,7 @@ purge:
rm -rf $$(find . -name .pytest_cache)
rm -rf $(yarn cache dir)
-uninstall-src: # Uninstalls source extensions if they're still installed
- - jupyter labextension unlink --no-build @elyra/services
- - jupyter labextension unlink --no-build @elyra/ui-components
- - jupyter labextension unlink --no-build @elyra/metadata-common
- - jupyter labextension unlink --no-build @elyra/script-editor
- - jupyter labextension uninstall --no-build @elyra/theme-extension
- - jupyter labextension uninstall --no-build @elyra/code-snippet-extension
- - jupyter labextension uninstall --no-build @elyra/metadata-extension
- - jupyter labextension uninstall --no-build @elyra/pipeline-editor-extension
- - jupyter labextension uninstall --no-build @elyra/python-editor-extension
- - jupyter labextension uninstall --no-build @elyra/r-editor-extension
- - jupyter labextension uninstall --no-build @elyra/scala-editor-extension
- - jupyter labextension uninstall --no-build @elyra/code-viewer-extension
- - jupyter labextension uninstall --no-build @elyra/script-debugger-extension
- - jupyter labextension unlink --no-build @elyra/pipeline-services
- - jupyter labextension unlink --no-build @elyra/pipeline-editor
-
-uninstall: uninstall-src
+uninstall:
$(PYTHON_PIP) uninstall -y jupyterlab-git
$(PYTHON_PIP) uninstall -y nbdime
$(PYTHON_PIP) uninstall -y jupyter-lsp
@@ -179,7 +162,7 @@ uninstall-server-package:
@$(PYTHON_PIP) uninstall elyra -y
install-server-package: uninstall-server-package
- $(PYTHON_PIP) install --upgrade --upgrade-strategy $(UPGRADE_STRATEGY) "$(shell find dist -name "elyra-*-py3-none-any.whl")[kfp-tekton]"
+ $(PYTHON_PIP) install --upgrade --upgrade-strategy $(UPGRADE_STRATEGY) "$(shell find dist -name "elyra-*-py3-none-any.whl")"
install-server: build-dependencies lint-server build-server install-server-package ## Build and install backend
@@ -189,7 +172,7 @@ install-all: package-ui install-server install-examples install-gitlab-dependenc
install-dev: package-ui-dev install-server install-examples install-gitlab-dependency check-install
-install-examples: ## Install example pipeline components
+install-examples: ## Install example pipeline components
# install Kubeflow Pipelines example components
# -> https://github.com/elyra-ai/examples/tree/main/component-catalog-connectors/kfp-example-components-connector
- $(PYTHON_PIP) install --upgrade elyra-examples-kfp-catalog
diff --git a/build_requirements.txt b/build_requirements.txt
index 2307f89b6..d16f92197 100644
--- a/build_requirements.txt
+++ b/build_requirements.txt
@@ -1,3 +1,3 @@
-jupyterlab>=4.0,<4.1
+jupyterlab==4.2.5
jupyter-packaging>=0.10
build
diff --git a/docs/requirements.txt b/docs/requirements.txt
index c92959e15..9ae7be90e 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,5 +1,5 @@
sphinx>=1.8
-sphinx_rtd_theme
+sphinx_rtd_theme==2.0.0
recommonmark>=0.6.0
sphinx-markdown-tables>=0.0.16
sphinx-version-warning
diff --git a/elyra/metadata/schemas/airflow.json b/elyra/metadata/schemas/airflow.json
index cbf967672..8ae7f329c 100644
--- a/elyra/metadata/schemas/airflow.json
+++ b/elyra/metadata/schemas/airflow.json
@@ -107,7 +107,7 @@
"description": "Token that has permission to push to the DAG repository",
"type": "string",
"uihints": {
- "ui:field": "password",
+ "ui:field": "@elyra/metadata-extension:plugin.password",
"category": "Apache Airflow"
}
},
@@ -161,7 +161,7 @@
"description": "Kubernetes secret that's defined in the specified user namespace, containing the Cloud Object Storage username and password. This property is required for authentication type KUBERNETES_SECRET.",
"type": "string",
"uihints": {
- "ui:field": "password",
+ "ui:field": "@elyra/metadata-extension:plugin.password",
"category": "Cloud Object Storage"
}
},
@@ -179,7 +179,7 @@
"type": "string",
"minLength": 8,
"uihints": {
- "ui:field": "password",
+ "ui:field": "@elyra/metadata-extension:plugin.password",
"category": "Cloud Object Storage"
}
},
@@ -193,7 +193,7 @@
"pattern": "^[^ \t]+([ \t]+[^ \t]+)*$"
},
"uihints": {
- "ui:field": "tags"
+ "ui:field": "@elyra/metadata-extension:plugin.tags"
}
}
},
diff --git a/elyra/metadata/schemas/code-snippet.json b/elyra/metadata/schemas/code-snippet.json
index aeeaa9a8b..c43e64caa 100644
--- a/elyra/metadata/schemas/code-snippet.json
+++ b/elyra/metadata/schemas/code-snippet.json
@@ -42,7 +42,7 @@
"pattern": "^[^ \t]+([ \t]+[^ \t]+)*$"
},
"uihints": {
- "ui:field": "tags"
+ "ui:field": "@elyra/metadata-extension:plugin.tags"
}
},
"language": {
@@ -50,7 +50,7 @@
"description": "Code snippet implementation language",
"type": "string",
"uihints": {
- "ui:field": "dropdown",
+ "ui:field": "@elyra/metadata-extension:plugin.dropdown",
"default_choices": ["Python", "Java", "R", "Scala", "Markdown"],
"category": "Source"
},
@@ -64,7 +64,7 @@
"type": "string"
},
"uihints": {
- "ui:field": "code",
+ "ui:field": "@elyra/metadata-extension:plugin.code",
"category": "Source"
}
}
diff --git a/elyra/metadata/schemas/kfp.json b/elyra/metadata/schemas/kfp.json
index bafe8b864..701549b44 100644
--- a/elyra/metadata/schemas/kfp.json
+++ b/elyra/metadata/schemas/kfp.json
@@ -106,7 +106,7 @@
"description": "Password or token to be used for authentication. This property is required for all authentication types, except NO_AUTHENTICATION and KUBERNETES_SERVICE_ACCOUNT_TOKEN.",
"type": "string",
"uihints": {
- "ui:field": "password",
+ "ui:field": "@elyra/metadata-extension:plugin.password",
"category": "Kubeflow Pipelines"
}
},
@@ -160,7 +160,7 @@
"description": "Kubernetes secret that's defined in the specified user namespace, containing the Cloud Object Storage username and password. This property is required for authentication type KUBERNETES_SECRET.",
"type": "string",
"uihints": {
- "ui:field": "password",
+ "ui:field": "@elyra/metadata-extension:plugin.password",
"category": "Cloud Object Storage"
}
},
@@ -178,7 +178,7 @@
"type": "string",
"minLength": 8,
"uihints": {
- "ui:field": "password",
+ "ui:field": "@elyra/metadata-extension:plugin.password",
"category": "Cloud Object Storage"
}
},
@@ -192,7 +192,7 @@
"pattern": "^[^ \t]+([ \t]+[^ \t]+)*$"
},
"uihints": {
- "ui:field": "tags"
+ "ui:field": "@elyra/metadata-extension:plugin.tags"
}
}
},
diff --git a/elyra/metadata/schemas/metadata-test.json b/elyra/metadata/schemas/metadata-test.json
index 084fd0420..7571df9ee 100644
--- a/elyra/metadata/schemas/metadata-test.json
+++ b/elyra/metadata/schemas/metadata-test.json
@@ -96,7 +96,7 @@
"type": "string",
"enum": ["elyra", "rocks"],
"uihints": {
- "ui:field": "dropdown",
+ "ui:field": "@elyra/metadata-extension:plugin.dropdown",
"default_choices": ["elyra"]
}
},
@@ -108,7 +108,7 @@
"maxItems": 10,
"uniqueItems": true,
"uihints": {
- "ui:field": "code"
+ "ui:field": "@elyra/metadata-extension:plugin.code"
}
},
"object_test": {
diff --git a/elyra/metadata/schemas/metadata-test2.json b/elyra/metadata/schemas/metadata-test2.json
index dd6293624..5c01718f7 100644
--- a/elyra/metadata/schemas/metadata-test2.json
+++ b/elyra/metadata/schemas/metadata-test2.json
@@ -86,7 +86,7 @@
"type": "string",
"enum": ["elyra", "rocks"],
"uihints": {
- "ui:field": "dropdown",
+ "ui:field": "@elyra/metadata-extension:plugin.dropdown",
"default_choices": ["elyra"]
}
},
@@ -98,7 +98,7 @@
"maxItems": 10,
"uniqueItems": true,
"uihints": {
- "ui:field": "code"
+ "ui:field": "@elyra/metadata-extension:plugin.code"
}
},
"object_test": {
diff --git a/elyra/metadata/schemas/runtime-image.json b/elyra/metadata/schemas/runtime-image.json
index 49c3bba00..93b997dcd 100644
--- a/elyra/metadata/schemas/runtime-image.json
+++ b/elyra/metadata/schemas/runtime-image.json
@@ -42,7 +42,7 @@
"pattern": "^[^ \t]+([ \t]+[^ \t]+)*$"
},
"uihints": {
- "ui:field": "tags"
+ "ui:field": "@elyra/metadata-extension:plugin.tags"
}
},
"image_name": {
@@ -72,7 +72,7 @@
"maxLength": 253,
"uihints": {
"category": "Source",
- "ui:field": "password"
+ "ui:field": "@elyra/metadata-extension:plugin.password"
}
}
},
diff --git a/elyra/metadata/schemas/url-catalog.json b/elyra/metadata/schemas/url-catalog.json
index 6532bf948..dcdd4d93c 100644
--- a/elyra/metadata/schemas/url-catalog.json
+++ b/elyra/metadata/schemas/url-catalog.json
@@ -84,7 +84,7 @@
"type": "string",
"minLength": 1,
"uihints": {
- "ui:field": "password",
+ "ui:field": "@elyra/metadata-extension:plugin.password",
"category": "Source credentials"
}
}
diff --git a/elyra/pipeline/airflow/package_catalog_connector/airflow-package-catalog.json b/elyra/pipeline/airflow/package_catalog_connector/airflow-package-catalog.json
index e2d89c96f..504a490ce 100644
--- a/elyra/pipeline/airflow/package_catalog_connector/airflow-package-catalog.json
+++ b/elyra/pipeline/airflow/package_catalog_connector/airflow-package-catalog.json
@@ -87,7 +87,7 @@
"type": "string",
"minLength": 1,
"uihints": {
- "ui:field": "password",
+ "ui:field": "@elyra/metadata-extension:plugin.password",
"category": "Source credentials"
}
}
diff --git a/elyra/pipeline/airflow/provider_package_catalog_connector/airflow-provider-package-catalog.json b/elyra/pipeline/airflow/provider_package_catalog_connector/airflow-provider-package-catalog.json
index 34cbbc92c..a57dc74da 100644
--- a/elyra/pipeline/airflow/provider_package_catalog_connector/airflow-provider-package-catalog.json
+++ b/elyra/pipeline/airflow/provider_package_catalog_connector/airflow-provider-package-catalog.json
@@ -79,7 +79,7 @@
"type": "string",
"minLength": 1,
"uihints": {
- "ui:field": "password",
+ "ui:field": "@elyra/metadata-extension:plugin.password",
"category": "Source credentials"
}
}
diff --git a/elyra/pipeline/kfp/PipelineConf.py b/elyra/pipeline/kfp/PipelineConf.py
new file mode 100644
index 000000000..ddf3fea38
--- /dev/null
+++ b/elyra/pipeline/kfp/PipelineConf.py
@@ -0,0 +1,158 @@
+from typing import Union
+
+from kubernetes.client.models import V1PodDNSConfig
+
+
+class PipelineConf:
+ """PipelineConf contains pipeline level settings."""
+
+ def __init__(self):
+ self.image_pull_secrets = []
+ self.timeout = 0
+ self.ttl_seconds_after_finished = -1
+ self._pod_disruption_budget_min_available = None
+ self.op_transformers = []
+ self.default_pod_node_selector = {}
+ self.image_pull_policy = None
+ self.parallelism = None
+ self._data_passing_method = None
+ self.dns_config = None
+
+ def set_image_pull_secrets(self, image_pull_secrets):
+ """Configures the pipeline level imagepullsecret.
+
+ Args:
+ image_pull_secrets: a list of Kubernetes V1LocalObjectReference For
+ detailed description, check Kubernetes V1LocalObjectReference definition
+ https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1LocalObjectReference.md
+ """
+ self.image_pull_secrets = image_pull_secrets
+ return self
+
+ def set_timeout(self, seconds: int):
+ """Configures the pipeline level timeout.
+
+ Args:
+ seconds: number of seconds for timeout
+ """
+ self.timeout = seconds
+ return self
+
+ def set_parallelism(self, max_num_pods: int):
+ """Configures the max number of total parallel pods that can execute at
+ the same time in a workflow.
+
+ Args:
+ max_num_pods: max number of total parallel pods.
+ """
+ if max_num_pods < 1:
+ raise ValueError("Pipeline max_num_pods set to < 1, allowed values are > 0")
+
+ self.parallelism = max_num_pods
+ return self
+
+ def set_ttl_seconds_after_finished(self, seconds: int):
+ """Configures the ttl after the pipeline has finished.
+
+ Args:
+ seconds: number of seconds for the workflow to be garbage collected after
+ it is finished.
+ """
+ self.ttl_seconds_after_finished = seconds
+ return self
+
+ def set_pod_disruption_budget(self, min_available: Union[int, str]):
+ """PodDisruptionBudget holds the number of concurrent disruptions that
+ you allow for pipeline Pods.
+
+ Args:
+ min_available (Union[int, str]): An eviction is allowed if at least
+ "minAvailable" pods selected by "selector" will still be available after
+ the eviction, i.e. even in the absence of the evicted pod. So for
+ example you can prevent all voluntary evictions by specifying "100%".
+ "minAvailable" can be either an absolute number or a percentage.
+ """
+ self._pod_disruption_budget_min_available = min_available
+ return self
+
+ def set_default_pod_node_selector(self, label_name: str, value: str):
+ """Add a constraint for nodeSelector for a pipeline.
+
+ Each constraint is a key-value pair label.
+
+ For the container to be eligible to run on a node, the node must have each
+ of the constraints appeared as labels.
+
+ Args:
+ label_name: The name of the constraint label.
+ value: The value of the constraint label.
+ """
+ self.default_pod_node_selector[label_name] = value
+ return self
+
+ def set_image_pull_policy(self, policy: str):
+ """Configures the default image pull policy.
+
+ Args:
+ policy: the pull policy, has to be one of: Always, Never, IfNotPresent.
+ For more info:
+ https://github.com/kubernetes-client/python/blob/10a7f95435c0b94a6d949ba98375f8cc85a70e5a/kubernetes/docs/V1Container.md
+ """
+ self.image_pull_policy = policy
+ return self
+
+ def add_op_transformer(self, transformer):
+ """Configures the op_transformers which will be applied to all ops in
+ the pipeline. The ops can be ResourceOp, VolumeOp, or ContainerOp.
+
+ Args:
+ transformer: A function that takes a kfp Op as input and returns a kfp Op
+ """
+ self.op_transformers.append(transformer)
+
+ def set_dns_config(self, dns_config: V1PodDNSConfig):
+ """Set the dnsConfig to be given to each pod.
+
+ Args:
+ dns_config: Kubernetes V1PodDNSConfig For detailed description, check
+ Kubernetes V1PodDNSConfig definition
+ https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1PodDNSConfig.md
+
+ Example:
+ ::
+
+ import kfp
+ from kubernetes.client.models import V1PodDNSConfig, V1PodDNSConfigOption
+ pipeline_conf = kfp.dsl.PipelineConf()
+ pipeline_conf.set_dns_config(dns_config=V1PodDNSConfig(
+ nameservers=["1.2.3.4"],
+ options=[V1PodDNSConfigOption(name="ndots", value="2")],
+ ))
+ """
+ self.dns_config = dns_config
+
+ @property
+ def data_passing_method(self):
+ return self._data_passing_method
+
+ @data_passing_method.setter
+ def data_passing_method(self, value):
+ """Sets the object representing the method used for intermediate data
+ passing.
+
+ Example:
+ ::
+
+ from kfp.dsl import PipelineConf, data_passing_methods
+ from kubernetes.client.models import V1Volume, V1PersistentVolumeClaimVolumeSource
+ pipeline_conf = PipelineConf()
+ pipeline_conf.data_passing_method =
+ data_passing_methods.KubernetesVolume(
+ volume=V1Volume(
+ name='data',
+ persistent_volume_claim=V1PersistentVolumeClaimVolumeSource('data-volume'),
+ ),
+ path_prefix='artifact_data/',
+ )
+ """
+ self._data_passing_method = value
diff --git a/elyra/pipeline/kfp/kfp_authentication.py b/elyra/pipeline/kfp/kfp_authentication.py
index 3ca44519a..84b8b15e4 100644
--- a/elyra/pipeline/kfp/kfp_authentication.py
+++ b/elyra/pipeline/kfp/kfp_authentication.py
@@ -27,9 +27,9 @@
from typing import Tuple
from urllib.parse import urlsplit
-from kfp.auth import KF_PIPELINES_SA_TOKEN_ENV
-from kfp.auth import KF_PIPELINES_SA_TOKEN_PATH
-from kfp.auth import ServiceAccountTokenVolumeCredentials
+from kfp.client import KF_PIPELINES_SA_TOKEN_ENV
+from kfp.client import KF_PIPELINES_SA_TOKEN_PATH
+from kfp.client import ServiceAccountTokenVolumeCredentials
import requests
@@ -230,6 +230,7 @@ def authenticate(
"""
kf_url = urlsplit(api_endpoint)._replace(path="").geturl()
+ kf_pipelines_ssl_sa_cert = os.getenv("PIPELINES_SSL_SA_CERTS", None)
# return data structure for successful requests
auth_info = {
@@ -239,6 +240,7 @@ def authenticate(
"cookies": None, # passed to KFP SDK client as "cookies" param value
"credentials": None, # passed to KFP SDK client as "credentials" param value
"existing_token": None, # passed to KFP SDK client as "existing_token" param value
+ "ssl_ca_cert": kf_pipelines_ssl_sa_cert, # passed to KFP SDK Client as "ssl_ca_cert" param value
}
try:
diff --git a/elyra/pipeline/kfp/processor_kfp.py b/elyra/pipeline/kfp/processor_kfp.py
index a0d32da53..33208ce83 100644
--- a/elyra/pipeline/kfp/processor_kfp.py
+++ b/elyra/pipeline/kfp/processor_kfp.py
@@ -39,19 +39,11 @@
from kfp import Client as ArgoClient
from kfp import compiler as kfp_argo_compiler
from kfp import components as components
-from kfp.dsl import PipelineConf
-from kfp.dsl import RUN_ID_PLACEHOLDER
from kubernetes import client as k8s_client
from traitlets import default
from traitlets import Unicode
-try:
- from kfp_tekton import compiler as kfp_tekton_compiler
- from kfp_tekton import TektonClient
-except ImportError:
- # We may not have kfp-tekton available and that's okay!
- kfp_tekton_compiler = None
- TektonClient = None
+RUN_ID_PLACEHOLDER = "{{workflow.uid}}"
from elyra._version import __version__
from elyra.metadata.schemaspaces import RuntimeImages
@@ -61,6 +53,7 @@
from elyra.pipeline.kfp.kfp_authentication import AuthenticationError
from elyra.pipeline.kfp.kfp_authentication import KFPAuthenticator
from elyra.pipeline.kfp.kfp_properties import KfpPipelineParameter
+from elyra.pipeline.kfp.PipelineConf import PipelineConf
from elyra.pipeline.pipeline import Operation
from elyra.pipeline.pipeline import Pipeline
from elyra.pipeline.processor import PipelineProcessor
@@ -173,11 +166,6 @@ def process(self, pipeline):
api_password = runtime_configuration.metadata.get("api_password")
user_namespace = runtime_configuration.metadata.get("user_namespace")
workflow_engine = WorkflowEngineType.get_instance_by_value(runtime_configuration.metadata.get("engine", "argo"))
- if workflow_engine == WorkflowEngineType.TEKTON and not TektonClient:
- raise ValueError(
- "Python package `kfp-tekton` is not installed. "
- "Please install using `elyra[kfp-tekton]` to use Tekton engine."
- )
# unpack Cloud Object Storage configs
cos_endpoint = runtime_configuration.metadata["cos_endpoint"]
@@ -206,22 +194,14 @@ def process(self, pipeline):
# Create Kubeflow Client
#############
try:
- if workflow_engine == WorkflowEngineType.TEKTON:
- client = TektonClient(
- host=api_endpoint,
- cookies=auth_info.get("cookies", None),
- credentials=auth_info.get("credentials", None),
- existing_token=auth_info.get("existing_token", None),
- namespace=user_namespace,
- )
- else:
- client = ArgoClient(
- host=api_endpoint,
- cookies=auth_info.get("cookies", None),
- credentials=auth_info.get("credentials", None),
- existing_token=auth_info.get("existing_token", None),
- namespace=user_namespace,
- )
+ client = ArgoClient(
+ host=api_endpoint,
+ cookies=auth_info.get("cookies", None),
+ credentials=auth_info.get("credentials", None),
+ existing_token=auth_info.get("existing_token", None),
+ namespace=user_namespace,
+ ssl_ca_cert=auth_info.get("ssl_ca_cert", None),
+ )
except Exception as ex:
# a common cause of these errors is forgetting to include `/pipeline` or including it with an 's'
api_endpoint_obj = urlsplit(api_endpoint)
@@ -275,7 +255,7 @@ def process(self, pipeline):
with tempfile.TemporaryDirectory() as temp_dir:
self.log.debug(f"Created temporary directory at: {temp_dir}")
- pipeline_path = os.path.join(temp_dir, f"{pipeline_name}.tar.gz")
+ pipeline_path = os.path.join(temp_dir, f"{pipeline_name}.yaml")
#############
# Get Pipeline ID
@@ -351,11 +331,15 @@ def process(self, pipeline):
)
# extract the ID of the pipeline we created
- pipeline_id = kfp_pipeline.id
+ pipeline_id = kfp_pipeline.pipeline_id
# the initial "pipeline version" has the same id as the pipeline itself
- version_id = pipeline_id
-
+ version_details = client.list_pipeline_versions(pipeline_id=pipeline_id)
+ version_list = version_details.pipeline_versions
+ if isinstance(version_list, list):
+ version_id = version_list[0].pipeline_version_id
+ else:
+ version_id = None
# CASE 2: pipeline already exists
else:
# upload the "pipeline version"
@@ -366,7 +350,7 @@ def process(self, pipeline):
)
# extract the id of the "pipeline version" that was created
- version_id = kfp_pipeline.id
+ version_id = kfp_pipeline.pipeline_version_id
except Exception as ex:
# a common cause of these errors is forgetting to include `/pipeline` or including it with an 's'
@@ -416,7 +400,10 @@ def process(self, pipeline):
# create pipeline run (or specified pipeline version)
run = client.run_pipeline(
- experiment_id=experiment.id, job_name=job_name, pipeline_id=pipeline_id, version_id=version_id
+ experiment_id=experiment.experiment_id,
+ job_name=job_name,
+ pipeline_id=pipeline_id,
+ version_id=version_id,
)
except Exception as ex:
@@ -435,7 +422,7 @@ def process(self, pipeline):
self.log_pipeline_info(
pipeline_name,
- f"pipeline submitted: {public_api_endpoint}/#/runs/details/{run.id}",
+ f"pipeline submitted: {public_api_endpoint}/{experiment.experiment_id}/runs/{run.run_id}",
duration=time.time() - t0,
)
@@ -450,8 +437,8 @@ def process(self, pipeline):
object_storage_path = None
return KfpPipelineProcessorResponse(
- run_id=run.id,
- run_url=f"{public_api_endpoint}/#/runs/details/{run.id}",
+ run_id=run.run_id,
+ run_url=f"{public_api_endpoint}/{experiment.experiment_id}/runs/{run.run_id}",
object_storage_url=object_storage_url,
object_storage_path=object_storage_path,
)
@@ -494,8 +481,6 @@ def export(
)
workflow_engine = WorkflowEngineType.get_instance_by_value(runtime_configuration.metadata.get("engine", "argo"))
- if workflow_engine == WorkflowEngineType.TEKTON and not TektonClient:
- raise ValueError("kfp-tekton not installed. Please install using elyra[kfp-tekton] to use Tekton engine.")
if Path(absolute_pipeline_export_path).exists() and not overwrite:
raise ValueError("File " + absolute_pipeline_export_path + " already exists.")
@@ -565,7 +550,7 @@ def _generate_pipeline_dsl(
code_generation_options = {}
# Load Kubeflow Pipelines Python DSL template
- loader = PackageLoader("elyra", "templates/kubeflow/v1")
+ loader = PackageLoader("elyra", "templates/kubeflow/v2")
template_env = Environment(loader=loader)
# Add filter that produces a Python-safe variable name
template_env.filters["python_safe"] = lambda x: re.sub(r"[" + re.escape(string.punctuation) + "\\s]", "_", x)
@@ -668,12 +653,7 @@ def _compile_pipeline_dsl(
# in the generated Python DSL "generated_pipeline"
pipeline_function = getattr(mod, "generated_pipeline")
# compile the DSL
- if workflow_engine == WorkflowEngineType.TEKTON:
- kfp_tekton_compiler.TektonCompiler().compile(
- pipeline_function, output_file, pipeline_conf=pipeline_conf
- )
- else:
- kfp_argo_compiler.Compiler().compile(pipeline_function, output_file, pipeline_conf=pipeline_conf)
+ kfp_argo_compiler.Compiler().compile(pipeline_function, output_file)
except Exception as ex:
raise RuntimeError(
f"Failed to compile pipeline with workflow_engine '{workflow_engine.value}' to '{output_file}'"
@@ -729,7 +709,7 @@ def _generate_workflow_tasks(
pipeline.pipeline_properties.get(pipeline_constants.COS_OBJECT_PREFIX), pipeline_instance_id
)
# - load the generic component definition template
- template_env = Environment(loader=PackageLoader("elyra", "templates/kubeflow/v1"))
+ template_env = Environment(loader=PackageLoader("elyra", "templates/kubeflow/v2"))
generic_component_template = template_env.get_template("generic_component_definition_template.jinja2")
# Add filter that escapes the " character in strings
template_env.filters["string_delimiter_safe"] = lambda string: re.sub('"', '\\\\\\\\"', string)
@@ -843,6 +823,11 @@ def _generate_workflow_tasks(
"size": operation.memory,
"units": "G",
}
+ workflow_task["task_modifiers"]["cpu_limit"] = operation.cpu_limit
+ workflow_task["task_modifiers"]["memory_limit"] = {
+ "size": operation.memory_limit,
+ "units": "G",
+ }
gpu_vendor = "nvidia.com/gpu"
if operation.gpu_vendor:
gpu_vendor = operation.gpu_vendor
@@ -920,13 +905,13 @@ def _generate_workflow_tasks(
# Identify task inputs and outputs using the component spec
# If no data type was specified, string is assumed
factory_function = components.load_component_from_text(component.definition)
- for input in factory_function.component_spec.inputs or []:
- sanitized_input_name = self._sanitize_param_name(input.name)
+ for input_key, input_value in (factory_function.component_spec.inputs or {}).items():
+ sanitized_input_name = self._sanitize_param_name(input_key)
workflow_task["task_inputs"][sanitized_input_name] = {
"value": None,
"task_output_reference": None,
"pipeline_parameter_reference": None,
- "data_type": (input.type or "string").lower(),
+ "data_type": (input_value.type or "string").lower(),
}
# Determine whether the value needs to be rendered in quotes
# in the generated DSL code. For example "my name" (string), and 34 (integer).
@@ -938,9 +923,9 @@ def _generate_workflow_tasks(
"bool",
]
- for output in factory_function.component_spec.outputs or []:
- workflow_task["task_outputs"][self._sanitize_param_name(output.name)] = {
- "data_type": output.type,
+ for output_key, output_value in (factory_function.component_spec.outputs or {}).items():
+ workflow_task["task_outputs"][self._sanitize_param_name(output_key)] = {
+ "data_type": output_value.type,
}
# Iterate over component properties and assign values to
@@ -1073,7 +1058,7 @@ def _compose_container_command_args(
account whether the container will run in a CRI-O environment.
"""
elyra_github_org = os.getenv("ELYRA_GITHUB_ORG", "elyra-ai")
- elyra_github_branch = os.getenv("ELYRA_GITHUB_BRANCH", "main" if "dev" in __version__ else "v" + __version__)
+ elyra_github_branch = os.getenv("ELYRA_GITHUB_BRANCH", "main" if "dev" in __version__ else f"v{__version__}")
elyra_bootstrap_script_url = os.getenv(
"ELYRA_BOOTSTRAP_SCRIPT_URL",
f"https://raw.githubusercontent.com/{elyra_github_org}/elyra/{elyra_github_branch}/elyra/kfp/bootstrapper.py", # noqa E501
diff --git a/elyra/pipeline/pipeline_definition.py b/elyra/pipeline/pipeline_definition.py
index c49c0c29b..6c90c810f 100644
--- a/elyra/pipeline/pipeline_definition.py
+++ b/elyra/pipeline/pipeline_definition.py
@@ -265,6 +265,9 @@ def convert_pipeline_parameters(self, runtime_type_name: str) -> None:
if parameter_class is None:
return None # runtime type does not support parameters, skip
+ if not ElyraProperty.subclass_exists_for_property(parameter_class.property_id):
+ ElyraProperty.build_property_map()
+
# Convert pipeline parameters to runtime-specific instances
converted_value = ElyraProperty.create_instance(parameter_class.property_id, self.pipeline_parameters)
if converted_value is not None:
diff --git a/elyra/templates/kubeflow/v2/generic_component_definition_template.jinja2 b/elyra/templates/kubeflow/v2/generic_component_definition_template.jinja2
new file mode 100644
index 000000000..85c8eb086
--- /dev/null
+++ b/elyra/templates/kubeflow/v2/generic_component_definition_template.jinja2
@@ -0,0 +1,24 @@
+name: Run a file
+description: Run a Jupyter notebook or Python/R script
+{% if task_parameters %}
+inputs:
+{%- for parameter in task_parameters %}
+- {name: {{ parameter.name }}, type: {{ parameter.input_type.component_input_type }}{% if parameter.description %}, description: "{{ parameter.description | string_delimiter_safe}}"{% endif %}{% if parameter.default_value is not none %}, default: {% if parameter.selected_type == 'String' %} "{{ parameter.default_value|string_delimiter_safe }}"{% else %}{{ parameter.default_value }}{% endif %}{% endif %}, optional: {{ (not parameter.required)|tojson }}}
+{%- endfor %}
+{% endif %}
+implementation:
+ container:
+ image: {{ container_image }}
+ command: [sh, -c]
+ args:
+ - |
+ {%- for parameter in task_parameters %}
+ {{ parameter.name }}="${{ loop.index0 }}"
+ {%- endfor %}
+ {%- for command in command_args %}
+ sh -c "{{command}}"
+ {%- endfor %}
+
+ {%- for parameter in task_parameters %}
+ - {inputValue: {{ parameter.name }}}
+ {%- endfor %}
diff --git a/elyra/templates/kubeflow/v2/python_dsl_template.jinja2 b/elyra/templates/kubeflow/v2/python_dsl_template.jinja2
new file mode 100644
index 000000000..c5c812130
--- /dev/null
+++ b/elyra/templates/kubeflow/v2/python_dsl_template.jinja2
@@ -0,0 +1,152 @@
+#
+# Generated by Elyra {{ elyra_version }}
+#
+import kfp
+from kubernetes.client import *
+from kubernetes.client.models import *
+from kfp.kubernetes import secret, volume
+
+from typing import Optional
+
+# ------------------------------------------------------------------
+# kfp-kubernetes 1.1.0 is misisng these function, explicity using them,
+# TODO: remove these function once a new release of kfp-kubernetes is made.
+# ------------------------------------------------------------------
+
+from google.protobuf import json_format
+from kfp.dsl import PipelineTask
+from kfp.kubernetes import common
+from kfp.kubernetes import kubernetes_executor_config_pb2 as pb
+
+from kfp.kubernetes import add_toleration, add_pod_label, add_pod_annotation
+
+{# Load statements for custom components -#}
+{# component_hash = """""" -#}
+{# factory_hash = kfp.components.load_component_from_text(component_hash) -#}
+{% for hash, component_definition in component_definitions.items() %}
+component_def_{{ hash | python_safe }} = """
+{{ component_definition }}
+"""
+
+factory_{{ hash | python_safe }} = kfp.components.load_component_from_text(component_def_{{ hash | python_safe }})
+{% endfor %}
+
+{# Define pipeline -#}
+{% if pipeline_description %}
+@kfp.dsl.pipeline(name="{{ pipeline_name }}", description="{{ pipeline_description | string_delimiter_safe }}")
+{% else %}
+@kfp.dsl.pipeline(name="{{ pipeline_name }}")
+{% endif %}
+def generated_pipeline(
+{% if pipeline_parameters %}
+{% for parameter in pipeline_parameters %}
+ {{ parameter.name }}{% if parameter.input_type.type_hint %}: {{ parameter.input_type.type_hint }}{% endif %} = {{ parameter|param_val_to_python_var }},
+{% endfor %}
+{% endif %}
+):
+{% for workflow_task in workflow_tasks.values() %}
+ {% set task_name = "task_" + workflow_task.escaped_task_id %}
+ # Task for node '{{ workflow_task.name }}'
+ {{ task_name }} = factory_{{ workflow_task.component_definition_hash | python_safe }}(
+{% for task_input_name, task_input_spec in workflow_task.task_inputs.items() %}
+{% if task_input_spec.task_output_reference %}
+ {{ task_input_name }}=task_{{ task_input_spec.task_output_reference.task_id }}.outputs["{{ task_input_spec.task_output_reference.output_id }}"],
+{% elif task_input_spec.pipeline_parameter_reference %}
+ {{ task_input_name }}={{ task_input_spec.pipeline_parameter_reference }},
+{% elif task_input_spec.requires_quoted_rendering %}
+ {{ task_input_name }}="""{{ task_input_spec.value | string_delimiter_safe }}""",
+{% else %}
+ {{ task_input_name }}={{ task_input_spec.value }},
+{% endif %}
+{% endfor %}
+ )
+{% if workflow_task.task_modifiers.object_storage_secret %}
+ secret.use_secret_as_env({{ task_name }}, "{{ workflow_task.task_modifiers.object_storage_secret }}", { "AWS_ACCESS_KEY_ID": "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY": "AWS_SECRET_ACCESS_KEY" })
+{% endif %}
+ {{ task_name }}.set_display_name("{{ workflow_task.name | string_delimiter_safe }}")
+{% if workflow_task.doc %}
+ add_pod_annotation({{ task_name }}, "elyra/node-user-doc","""{{ workflow_task.doc| string_delimiter_safe }}""")
+{% endif %}
+{% if workflow_task.task_modifiers.cpu_request %}
+ {{ task_name }}.set_cpu_request(cpu="{{ workflow_task.task_modifiers.cpu_request }}")
+{% endif %}
+{% if workflow_task.task_modifiers.mem_request and workflow_task.task_modifiers.mem_request.size %}
+ {{ task_name }}.set_memory_request(memory="{{ workflow_task.task_modifiers.mem_request.size }}{{ workflow_task.task_modifiers.mem_request.units }}")
+{% endif %}
+{% if workflow_task.task_modifiers.cpu_limit %}
+ {{ task_name }}.set_cpu_limit(cpu="{{ workflow_task.task_modifiers.cpu_limit }}")
+{% endif %}
+{% if workflow_task.task_modifiers.memory_limit and workflow_task.task_modifiers.memory_limit.size %}
+ {{ task_name }}.set_memory_limit(memory="{{ workflow_task.task_modifiers.memory_limit.size }}{{ workflow_task.task_modifiers.memory_limit.units }}")
+{% endif %}
+{% if workflow_task.task_modifiers.gpu_limit and workflow_task.task_modifiers.gpu_limit.size %}
+ {{ task_name }}.set_accelerator_limit("{{ workflow_task.task_modifiers.gpu_limit.size }}").set_accelerator_type("{{ workflow_task.task_modifiers.gpu_limit.vendor }}")
+{% endif %}
+{% if workflow_task.task_modifiers.env_variables %}
+{% for env_var_name, env_var_value in workflow_task.task_modifiers.env_variables.items() %}
+ {{ task_name }}.set_env_variable(name="{{ env_var_name }}", value="{{ env_var_value | string_delimiter_safe }}")
+{% endfor %}
+{% endif %}
+{% if workflow_task.task_modifiers.set_run_name %}
+ {{ task_name }}.set_env_variable(name="ELYRA_RUN_NAME", value="{{ workflow_task.task_modifiers.set_run_name }}")
+{% endif %}
+{% if workflow_task.task_modifiers.disable_node_caching %}
+ {{ task_name }}.execution_options.caching_strategy.max_cache_staleness = "P0D"
+{% endif %}
+{% if workflow_task.task_modifiers.pod_labels %}
+{% for pod_label_key, pod_label_value in workflow_task.task_modifiers.pod_labels.items() %}
+ add_pod_label({{ task_name }}, "{{ pod_label_key }}", "{{ pod_label_value }}")
+{% endfor %}
+{% endif %}
+{% if workflow_task.task_modifiers.pod_annotations %}
+{% for pod_annotation_key, pod_annotation_value in workflow_task.task_modifiers.pod_annotations.items() %}
+ add_pod_annotation({{ task_name }}, "{{ pod_annotation_key }}" , """{{ pod_annotation_value | string_delimiter_safe }}""")
+{% endfor %}
+{% endif %}
+{% if workflow_task.task_modifiers.kubernetes_secrets %}
+{% for env_var, secret_dict in workflow_task.task_modifiers.kubernetes_secrets.items() %}
+ secret.use_secret_as_env({{ task_name }}, "{{ secret_dict.name }}", { "{{ secret_dict.key }}" : "{{ env_var }}" })
+{% endfor %}
+{% endif %}
+{% if workflow_task.task_modifiers.kubernetes_volumes %}
+{% for volume_path, volume_dict in workflow_task.task_modifiers.kubernetes_volumes.items() %}
+ volume.mount_pvc({{ task_name }}, "{{ volume_dict.pvc_name }}", "{{ volume_path }}")
+{% endfor %}
+{% endif %}
+{% if workflow_task.task_modifiers.kubernetes_tolerations %}
+{% for toleration_dict in workflow_task.task_modifiers.kubernetes_tolerations.values() %}
+ add_toleration(
+ {{ task_name }},
+ {% if toleration_dict.key %}
+ key="{{ toleration_dict.key }}",
+ {% else %}
+ key=None,
+ {% endif %}
+ operator="{{ toleration_dict.operator }}",
+ {% if toleration_dict.value %}
+ value="{{ toleration_dict.value | string_delimiter_safe }}",
+ {% else %}
+ value=None,
+ {% endif %}
+ {% if toleration_dict.effect %}
+ effect="{{ toleration_dict.effect }}",
+ {% else %}
+ effect=None,
+ {% endif %}
+ )
+{% endfor %}
+{% endif %}
+{# declare upstream dependencies -#}
+{% if workflow_task.upstream_workflow_task_ids %}
+{% for upstream_workflow_task_id in workflow_task.upstream_workflow_task_ids %}
+ {{ task_name }}.after(task_{{ upstream_workflow_task_id | python_safe }})
+{% endfor %}
+{% endif %}
+{% endfor %}
+
+if __name__ == "__main__":
+ from pathlib import Path
+ kfp.compiler.Compiler().compile(
+ pipeline_func=generated_pipeline,
+ package_path=Path(__file__).with_suffix(".yaml").name,
+ )
\ No newline at end of file
diff --git a/elyra/tests/cli/resources/pipelines/kf_inputpath_parameter.pipeline b/elyra/tests/cli/resources/pipelines/kf_inputpath_parameter.pipeline
index 36a693701..9ddfc3247 100644
--- a/elyra/tests/cli/resources/pipelines/kf_inputpath_parameter.pipeline
+++ b/elyra/tests/cli/resources/pipelines/kf_inputpath_parameter.pipeline
@@ -16,7 +16,7 @@
"component_parameters": {
"url": {
"activeControl": "StringControl",
- "StringControl": "https://raw.githubusercontent.com/kubeflow/pipelines/93fc34474bf989998cf19445149aca2847eee763/components/notebooks/samples/test_notebook.ipynb"
+ "StringControl": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/notebooks/samples/test_notebook.ipynb"
},
"curl_options": {
"activeControl": "StringControl",
@@ -25,7 +25,7 @@
"output_data": ""
},
"label": "",
- "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/1.6.0/components/web/Download/component.yaml",
+ "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/web/Download/component-sdk-v2.yaml",
"ui_data": {
"label": "Download data",
"image": "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20276.93%20274.55%22%3E%3Cg%20id%3D%22Layer_2%22%20data-name%3D%22Layer%202%22%3E%3Cg%20id%3D%22Layer_1-2%22%20data-name%3D%22Layer%201%22%3E%3Cpath%20d%3D%22M95.9%2C62.15%2C100%2C164.25l73.75-94.12a6.79%2C6.79%2C0%2C0%2C1%2C9.6-1.11l46%2C36.92-15-65.61Z%22%20fill%3D%22%234279f4%22%2F%3E%3Cpolygon%20points%3D%22102.55%20182.98%20167.97%20182.98%20127.8%20150.75%20102.55%20182.98%22%20fill%3D%22%230028aa%22%2F%3E%3Cpolygon%20points%3D%22180.18%2083.92%20136.18%20140.06%20183.06%20177.67%20227.53%20121.91%20180.18%2083.92%22%20fill%3D%22%23014bd1%22%2F%3E%3Cpolygon%20points%3D%2283.56%2052.3%2083.57%2052.29%20122.26%203.77%2059.87%2033.82%2044.46%20101.33%2083.56%2052.3%22%20fill%3D%22%23bedcff%22%2F%3E%3Cpolygon%20points%3D%2245.32%20122.05%2086.76%20174.01%2082.81%2075.03%2045.32%20122.05%22%20fill%3D%22%236ca1ff%22%2F%3E%3Cpolygon%20points%3D%22202.31%2028.73%20142.65%200%20105.52%2046.56%20202.31%2028.73%22%20fill%3D%22%23a1c3ff%22%2F%3E%3Cpath%20d%3D%22M1.6%2C272V227.22H7.34v23.41l20.48-23.41h6.4l-17.39%2C19.7%2C19%2C25.07H29.1l-15.92-20.8-5.84%2C6.65V272Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M41.62%2C262.21V240h5.43v22.39a4.67%2C4.67%2C0%2C0%2C0%2C2.35%2C4.19%2C11%2C11%2C0%2C0%2C0%2C11%2C0%2C4.69%2C4.69%2C0%2C0%2C0%2C2.33-4.19V240h5.43v22.19a9.08%2C9.08%2C0%2C0%2C1-4.1%2C7.87%2C16.2%2C16.2%2C0%2C0%2C1-18.37%2C0A9.07%2C9.07%2C0%2C0%2C1%2C41.62%2C262.21Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M77.46%2C272V224h5.43v16.81a29.29%2C29.29%2C0%2C0%2C1%2C9.32-1.73%2C13.1%2C13.1%2C0%2C0%2C1%2C6.2%2C1.41%2C10.71%2C10.71%2C0%2C0%2C1%2C4.18%2C3.74%2C18.07%2C18.07%2C0%2C0%2C1%2C2.23%2C5.06%2C21.26%2C21.26%2C0%2C0%2C1%2C.73%2C5.58q0%2C8.43-4.38%2C12.79T87.35%2C272Zm5.43-4.87h4.55q6.77%2C0%2C9.72-2.95t3-9.51a14.21%2C14.21%2C0%2C0%2C0-2-7.52%2C6.55%2C6.55%2C0%2C0%2C0-6-3.22%2C24.73%2C24.73%2C0%2C0%2C0-9.25%2C1.54Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M112.36%2C255.94q0-7.71%2C4.09-12.3a13.75%2C13.75%2C0%2C0%2C1%2C10.8-4.59q13.35%2C0%2C13.36%2C18.86H117.79a12.3%2C12.3%2C0%2C0%2C0%2C2.9%2C7.07q2.59%2C3.11%2C7.9%2C3.1a24.92%2C24.92%2C0%2C0%2C0%2C10.55-2v5a27.74%2C27.74%2C0%2C0%2C1-9.86%2C1.87%2C19.83%2C19.83%2C0%2C0%2C1-7.7-1.37%2C13.31%2C13.31%2C0%2C0%2C1-5.28-3.76%2C16.21%2C16.21%2C0%2C0%2C1-3-5.38A20.84%2C20.84%2C0%2C0%2C1%2C112.36%2C255.94Zm5.62-2.12h17.26a14.91%2C14.91%2C0%2C0%2C0-2.37-7.12%2C6.44%2C6.44%2C0%2C0%2C0-5.62-2.78%2C8.2%2C8.2%2C0%2C0%2C0-6.21%2C2.72A12.07%2C12.07%2C0%2C0%2C0%2C118%2C253.82Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M147.32%2C244.89V240h5v-7.59a8.14%2C8.14%2C0%2C0%2C1%2C2.31-6.05%2C7.79%2C7.79%2C0%2C0%2C1%2C5.69-2.28h7.86V229h-5c-2.21%2C0-3.67.45-4.37%2C1.34s-1.06%2C2.55-1.06%2C5V240h8.46v4.87h-8.46V272h-5.44v-27.1Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M175.26%2C272V224h5.43v48Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M194.41%2C268.05a17.86%2C17.86%2C0%2C1%2C1%2C12.33%2C4.9A16.57%2C16.57%2C0%2C0%2C1%2C194.41%2C268.05Zm3.84-20.65a13.16%2C13.16%2C0%2C0%2C0%2C0%2C17.2%2C12.07%2C12.07%2C0%2C0%2C0%2C17%2C0%2C13.09%2C13.09%2C0%2C0%2C0%2C0-17.2%2C12.07%2C12.07%2C0%2C0%2C0-17%2C0Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M228.45%2C240h5.75l7.3%2C25.32L248.93%2C240h5.36l7.34%2C25.34L269%2C240h5.74L264.7%2C272h-6.12l-6.83-24.58L245%2C272h-6.47Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E",
@@ -150,7 +150,7 @@
"output_hash": ""
},
"label": "",
- "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/1.6.0/components/basics/Calculate_hash/component.yaml",
+ "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/basics/Calculate_hash/component.yaml",
"ui_data": {
"label": "Calculate data hash",
"image": "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20276.93%20274.55%22%3E%3Cg%20id%3D%22Layer_2%22%20data-name%3D%22Layer%202%22%3E%3Cg%20id%3D%22Layer_1-2%22%20data-name%3D%22Layer%201%22%3E%3Cpath%20d%3D%22M95.9%2C62.15%2C100%2C164.25l73.75-94.12a6.79%2C6.79%2C0%2C0%2C1%2C9.6-1.11l46%2C36.92-15-65.61Z%22%20fill%3D%22%234279f4%22%2F%3E%3Cpolygon%20points%3D%22102.55%20182.98%20167.97%20182.98%20127.8%20150.75%20102.55%20182.98%22%20fill%3D%22%230028aa%22%2F%3E%3Cpolygon%20points%3D%22180.18%2083.92%20136.18%20140.06%20183.06%20177.67%20227.53%20121.91%20180.18%2083.92%22%20fill%3D%22%23014bd1%22%2F%3E%3Cpolygon%20points%3D%2283.56%2052.3%2083.57%2052.29%20122.26%203.77%2059.87%2033.82%2044.46%20101.33%2083.56%2052.3%22%20fill%3D%22%23bedcff%22%2F%3E%3Cpolygon%20points%3D%2245.32%20122.05%2086.76%20174.01%2082.81%2075.03%2045.32%20122.05%22%20fill%3D%22%236ca1ff%22%2F%3E%3Cpolygon%20points%3D%22202.31%2028.73%20142.65%200%20105.52%2046.56%20202.31%2028.73%22%20fill%3D%22%23a1c3ff%22%2F%3E%3Cpath%20d%3D%22M1.6%2C272V227.22H7.34v23.41l20.48-23.41h6.4l-17.39%2C19.7%2C19%2C25.07H29.1l-15.92-20.8-5.84%2C6.65V272Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M41.62%2C262.21V240h5.43v22.39a4.67%2C4.67%2C0%2C0%2C0%2C2.35%2C4.19%2C11%2C11%2C0%2C0%2C0%2C11%2C0%2C4.69%2C4.69%2C0%2C0%2C0%2C2.33-4.19V240h5.43v22.19a9.08%2C9.08%2C0%2C0%2C1-4.1%2C7.87%2C16.2%2C16.2%2C0%2C0%2C1-18.37%2C0A9.07%2C9.07%2C0%2C0%2C1%2C41.62%2C262.21Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M77.46%2C272V224h5.43v16.81a29.29%2C29.29%2C0%2C0%2C1%2C9.32-1.73%2C13.1%2C13.1%2C0%2C0%2C1%2C6.2%2C1.41%2C10.71%2C10.71%2C0%2C0%2C1%2C4.18%2C3.74%2C18.07%2C18.07%2C0%2C0%2C1%2C2.23%2C5.06%2C21.26%2C21.26%2C0%2C0%2C1%2C.73%2C5.58q0%2C8.43-4.38%2C12.79T87.35%2C272Zm5.43-4.87h4.55q6.77%2C0%2C9.72-2.95t3-9.51a14.21%2C14.21%2C0%2C0%2C0-2-7.52%2C6.55%2C6.55%2C0%2C0%2C0-6-3.22%2C24.73%2C24.73%2C0%2C0%2C0-9.25%2C1.54Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M112.36%2C255.94q0-7.71%2C4.09-12.3a13.75%2C13.75%2C0%2C0%2C1%2C10.8-4.59q13.35%2C0%2C13.36%2C18.86H117.79a12.3%2C12.3%2C0%2C0%2C0%2C2.9%2C7.07q2.59%2C3.11%2C7.9%2C3.1a24.92%2C24.92%2C0%2C0%2C0%2C10.55-2v5a27.74%2C27.74%2C0%2C0%2C1-9.86%2C1.87%2C19.83%2C19.83%2C0%2C0%2C1-7.7-1.37%2C13.31%2C13.31%2C0%2C0%2C1-5.28-3.76%2C16.21%2C16.21%2C0%2C0%2C1-3-5.38A20.84%2C20.84%2C0%2C0%2C1%2C112.36%2C255.94Zm5.62-2.12h17.26a14.91%2C14.91%2C0%2C0%2C0-2.37-7.12%2C6.44%2C6.44%2C0%2C0%2C0-5.62-2.78%2C8.2%2C8.2%2C0%2C0%2C0-6.21%2C2.72A12.07%2C12.07%2C0%2C0%2C0%2C118%2C253.82Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M147.32%2C244.89V240h5v-7.59a8.14%2C8.14%2C0%2C0%2C1%2C2.31-6.05%2C7.79%2C7.79%2C0%2C0%2C1%2C5.69-2.28h7.86V229h-5c-2.21%2C0-3.67.45-4.37%2C1.34s-1.06%2C2.55-1.06%2C5V240h8.46v4.87h-8.46V272h-5.44v-27.1Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M175.26%2C272V224h5.43v48Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M194.41%2C268.05a17.86%2C17.86%2C0%2C1%2C1%2C12.33%2C4.9A16.57%2C16.57%2C0%2C0%2C1%2C194.41%2C268.05Zm3.84-20.65a13.16%2C13.16%2C0%2C0%2C0%2C0%2C17.2%2C12.07%2C12.07%2C0%2C0%2C0%2C17%2C0%2C13.09%2C13.09%2C0%2C0%2C0%2C0-17.2%2C12.07%2C12.07%2C0%2C0%2C0-17%2C0Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M228.45%2C240h5.75l7.3%2C25.32L248.93%2C240h5.36l7.34%2C25.34L269%2C240h5.74L264.7%2C272h-6.12l-6.83-24.58L245%2C272h-6.47Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E",
diff --git a/elyra/tests/cli/test_pipeline_app.py b/elyra/tests/cli/test_pipeline_app.py
index c043abe9e..3ae1b25e8 100644
--- a/elyra/tests/cli/test_pipeline_app.py
+++ b/elyra/tests/cli/test_pipeline_app.py
@@ -1079,6 +1079,9 @@ def test_export_incompatible_runtime_config(kubeflow_pipelines_runtime_instance,
@pytest.mark.parametrize("catalog_instance_no_server_process", [KFP_COMPONENT_CACHE_INSTANCE], indirect=True)
+@pytest.mark.skip(
+ reason="This test is not compatible with KFP v2: It relies on incompatible assets from elyra-examples-kfp-catalog lib. See https://github.com/elyra-ai/examples/issues/115 and https://github.com/opendatahub-io/elyra-examples/pull/1" # noqa: E501
+)
def test_export_kubeflow_output_option(
jp_environ, kubeflow_pipelines_runtime_instance, catalog_instance_no_server_process
):
@@ -1229,6 +1232,9 @@ def test_export_airflow_output_option(airflow_runtime_instance):
@pytest.mark.parametrize("catalog_instance_no_server_process", [KFP_COMPONENT_CACHE_INSTANCE], indirect=True)
+@pytest.mark.skip(
+ reason="This test is not compatible with KFP v2: It relies on incompatible assets from elyra-examples-kfp-catalog lib. See https://github.com/elyra-ai/examples/issues/115 and https://github.com/opendatahub-io/elyra-examples/pull/1" # noqa: E501
+)
def test_export_kubeflow_overwrite_option(
jp_environ, kubeflow_pipelines_runtime_instance, catalog_instance_no_server_process
):
@@ -1322,6 +1328,9 @@ def test_export_airflow_format_option(airflow_runtime_instance):
@pytest.mark.parametrize("catalog_instance_no_server_process", [KFP_COMPONENT_CACHE_INSTANCE], indirect=True)
+@pytest.mark.skip(
+ reason="This test is not compatible with KFP v2: It relies on incompatible assets from elyra-examples-kfp-catalog lib. See https://github.com/elyra-ai/examples/issues/115 and https://github.com/opendatahub-io/elyra-examples/pull/1" # noqa: E501
+)
def test_export_kubeflow_format_option(
jp_environ, kubeflow_pipelines_runtime_instance, catalog_instance_no_server_process
):
diff --git a/elyra/tests/metadata/test_metadata.py b/elyra/tests/metadata/test_metadata.py
index 71728dd3a..d431d8428 100644
--- a/elyra/tests/metadata/test_metadata.py
+++ b/elyra/tests/metadata/test_metadata.py
@@ -704,7 +704,7 @@ def test_validation_performance():
print(
f"\nMemory: {diff:,} kb, Start: {memory_start.rss / 1024 / 1024:,.3f} mb, "
f"End: {memory_end.rss / 1024 / 1024:,.3f} mb., "
- f"Elapsed time: {t1-t0:.3f}s over {iterations} iterations."
+ f"Elapsed time: {t1 - t0:.3f}s over {iterations} iterations."
)
diff --git a/elyra/tests/pipeline/kfp/test_processor_kfp.py b/elyra/tests/pipeline/kfp/test_processor_kfp.py
index f57040b78..6ab857d2e 100644
--- a/elyra/tests/pipeline/kfp/test_processor_kfp.py
+++ b/elyra/tests/pipeline/kfp/test_processor_kfp.py
@@ -15,37 +15,25 @@
#
from datetime import datetime
import hashlib
-import json
import os
from pathlib import Path
import re
from typing import Any
from typing import Dict
-from kfp.dsl import RUN_ID_PLACEHOLDER
import pytest
import yaml
from elyra.pipeline.catalog_connector import FilesystemComponentCatalogConnector
from elyra.pipeline.component import Component
from elyra.pipeline.kfp.kfp_properties import KfpPipelineParameter
-from elyra.pipeline.kfp.processor_kfp import CRIO_VOL_DEF_MEDIUM
-from elyra.pipeline.kfp.processor_kfp import CRIO_VOL_DEF_NAME
-from elyra.pipeline.kfp.processor_kfp import CRIO_VOL_DEF_SIZE
-from elyra.pipeline.kfp.processor_kfp import CRIO_VOL_MOUNT_PATH
-from elyra.pipeline.kfp.processor_kfp import CRIO_VOL_PYTHON_PATH
-from elyra.pipeline.kfp.processor_kfp import CRIO_VOL_WORKDIR_PATH
from elyra.pipeline.kfp.processor_kfp import KfpPipelineProcessor
+from elyra.pipeline.kfp.processor_kfp import RUN_ID_PLACEHOLDER
from elyra.pipeline.kfp.processor_kfp import WorkflowEngineType
from elyra.pipeline.pipeline import GenericOperation
from elyra.pipeline.pipeline import Operation
from elyra.pipeline.pipeline import Pipeline
-from elyra.pipeline.pipeline_constants import COS_OBJECT_PREFIX
-from elyra.pipeline.pipeline_constants import KUBERNETES_POD_ANNOTATIONS
-from elyra.pipeline.pipeline_constants import KUBERNETES_POD_LABELS
-from elyra.pipeline.pipeline_constants import KUBERNETES_SECRETS
from elyra.pipeline.pipeline_constants import KUBERNETES_SHARED_MEM_SIZE
-from elyra.pipeline.pipeline_constants import KUBERNETES_TOLERATIONS
from elyra.pipeline.pipeline_constants import MOUNTED_VOLUMES
from elyra.pipeline.processor import PipelineProcessor
from elyra.pipeline.properties import ComponentProperty
@@ -57,7 +45,6 @@
from elyra.pipeline.properties import KubernetesSecret
from elyra.pipeline.properties import KubernetesToleration
from elyra.pipeline.properties import VolumeMount
-from elyra.util.cos import join_paths
from elyra.util.kubernetes import sanitize_label_value
PIPELINE_FILE_COMPLEX = str((Path("resources") / "sample_pipelines" / "pipeline_dependency_complex.json").as_posix())
@@ -491,40 +478,8 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_custom_component_pipeline(
with open(compiled_argo_output_file) as fh:
argo_spec = yaml.safe_load(fh.read())
- assert "argoproj.io/" in argo_spec["apiVersion"]
- pipeline_spec_annotations = json.loads(argo_spec["metadata"]["annotations"]["pipelines.kubeflow.org/pipeline_spec"])
- assert (
- pipeline_spec_annotations["name"] == pipeline.name
- ), f"DSL input: {generated_argo_dsl}\nArgo output: {argo_spec}"
- assert pipeline_spec_annotations["description"] == pipeline.description, pipeline_spec_annotations
-
- # generate Python DSL for the Tekton workflow engine
- generated_tekton_dsl = processor._generate_pipeline_dsl(
- pipeline=pipeline, pipeline_name=pipeline.name, workflow_engine=WorkflowEngineType.TEKTON
- )
-
- assert generated_tekton_dsl is not None
- # Generated DSL includes workflow engine specific code in the _main_ function
- assert "compiler.TektonCompiler().compile(" in generated_tekton_dsl
-
- compiled_tekton_output_file = Path(tmpdir) / "compiled_kfp_test_tekton.yaml"
-
- # if the compiler discovers an issue with the generated DSL this call fails
- processor._compile_pipeline_dsl(
- dsl=generated_tekton_dsl,
- workflow_engine=WorkflowEngineType.TEKTON,
- output_file=compiled_tekton_output_file.as_posix(),
- pipeline_conf=None,
- )
-
- # verify that the output file exists
- assert compiled_tekton_output_file.is_file()
-
- # verify the file content
- with open(compiled_tekton_output_file) as fh:
- tekton_spec = yaml.safe_load(fh.read())
-
- assert "tekton.dev/" in tekton_spec["apiVersion"]
+ assert argo_spec["pipelineInfo"]["name"] == pipeline.name
+ assert argo_spec["pipelineInfo"]["description"] == pipeline.description
@pytest.mark.parametrize(
@@ -539,15 +494,6 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_custom_component_pipeline(
/ "kfp-one-node-generic.pipeline",
"workflow_engine": WorkflowEngineType.ARGO,
},
- {
- "pipeline_file": Path(__file__).parent
- / ".."
- / "resources"
- / "test_pipelines"
- / "kfp"
- / "kfp-one-node-generic.pipeline",
- "workflow_engine": WorkflowEngineType.TEKTON,
- },
],
indirect=True,
)
@@ -614,13 +560,11 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_workflow_engine_test(
# Load compiled workflow
with open(compiled_output_file_name) as f:
- workflow_spec = yaml.safe_load(f.read())
+ workflow_spec_docs = list(yaml.safe_load_all(f.read()))
- # Verify that the output is for the specified workflow engine
- if workflow_engine == WorkflowEngineType.TEKTON:
- assert "tekton.dev/" in workflow_spec["apiVersion"]
- else:
- assert "argoproj.io/" in workflow_spec["apiVersion"]
+ assert len(workflow_spec_docs) == 2
+ assert "components" in workflow_spec_docs[0]
+ assert "platforms" in workflow_spec_docs[1]
@pytest.mark.parametrize(
@@ -634,17 +578,6 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_workflow_engine_test(
/ "kfp"
/ "kfp-one-node-generic.pipeline",
"workflow_engine": WorkflowEngineType.ARGO,
- "use_cos_credentials_secret": True,
- },
- {
- "pipeline_file": Path(__file__).parent
- / ".."
- / "resources"
- / "test_pipelines"
- / "kfp"
- / "kfp-one-node-generic.pipeline",
- "workflow_engine": WorkflowEngineType.ARGO,
- "use_cos_credentials_secret": False,
},
],
indirect=True,
@@ -672,7 +605,6 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_one_generic_node_pipeline_te
runtime_config = metadata_dependencies["runtime_config"]
assert runtime_config is not None
assert runtime_config.name == pipeline.runtime_config
- runtime_image_configs = metadata_dependencies["runtime_image_configs"]
workflow_engine = WorkflowEngineType.get_instance_by_value(runtime_config.metadata["engine"])
@@ -714,28 +646,26 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_one_generic_node_pipeline_te
pipeline_conf=None,
)
- # Load generated Argo workflow
+ # Load generated workflow
with open(compiled_argo_output_file_name) as f:
- argo_spec = yaml.safe_load(f.read())
+ spec_docs = list(yaml.safe_load_all(f.read()))
- # verify that this is an argo specification
- assert "argoproj.io" in argo_spec["apiVersion"]
+ assert len(spec_docs) == 2
+ components, platforms = spec_docs[0], spec_docs[1]["platforms"]
- pipeline_meta_annotations = json.loads(argo_spec["metadata"]["annotations"]["pipelines.kubeflow.org/pipeline_spec"])
- assert pipeline_meta_annotations["name"] == pipeline.name
- assert pipeline_meta_annotations["description"] == pipeline.description
+ assert components["pipelineInfo"]["name"] == pipeline.name
+ assert components["pipelineInfo"]["description"] == pipeline.description
# There should be two templates, one for the DAG and one for the generic node.
# Locate the one for the generic node and inspect its properties.
- assert len(argo_spec["spec"]["templates"]) == 2
- if argo_spec["spec"]["templates"][0]["name"] == argo_spec["spec"]["entrypoint"]:
- node_template = argo_spec["spec"]["templates"][1]
- else:
- node_template = argo_spec["spec"]["templates"][0]
+ assert components["root"]["dag"]
+ assert len(components["components"]) == 1
+ executors = components["deploymentSpec"]["executors"]
+ assert len(executors) == 1
+
+ node_template = components["deploymentSpec"]["executors"]["exec-run-a-file"]
# Verify component definition information (see generic_component_definition_template.jinja2)
- # - property 'name'
- assert node_template["name"] == "run-a-file"
# - property 'implementation.container.command'
assert node_template["container"]["command"] == ["sh", "-c"]
# - property 'implementation.container.args'
@@ -749,11 +679,7 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_one_generic_node_pipeline_te
# - the object storage bucket name that this node uses for file I/O
assert f"--cos-bucket '{runtime_config.metadata['cos_bucket']}'" in node_template["container"]["args"][0]
# - the directory within that object storage bucket
- if pipeline.pipeline_properties.get(COS_OBJECT_PREFIX):
- expected_directory_value = join_paths(pipeline.pipeline_properties.get(COS_OBJECT_PREFIX), pipeline_instance_id)
- assert f"--cos-directory '{expected_directory_value}' " in node_template["container"]["args"][0]
- else:
- assert f"--cos-directory '{pipeline_instance_id}" in node_template["container"]["args"][0]
+ assert f"--cos-directory '{pipeline_instance_id}" in node_template["container"]["args"][0]
# - the name of the archive in that directory
expected_archive_name = processor._get_dependency_archive_name(op)
assert f"--cos-dependencies-archive '{expected_archive_name}' " in node_template["container"]["args"][0]
@@ -770,33 +696,26 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_one_generic_node_pipeline_te
# - property 'implementation.container.image'
assert node_template["container"]["image"] == op.runtime_image
- # - property 'implementation.container.imagePullPolicy'
- # The image pull policy is defined in the the runtime image
- # configuration. Look it up and verified it is properly applied.
- for runtime_image_config in runtime_image_configs:
- if runtime_image_config.metadata["image_name"] == op.runtime_image:
- if runtime_image_config.metadata.get("pull_policy"):
- assert node_template["container"]["imagePullPolicy"] == runtime_image_config.metadata["pull_policy"]
- else:
- assert node_template["container"].get("imagePullPolicy") is None
- break
+
+ pod_metadata = platforms["kubernetes"]["deploymentSpec"]["executors"]["exec-run-a-file"]["podMetadata"]
+ assert pod_metadata
# Verify Kubernetes labels and annotations that Elyra attaches to pods that
# execute generic nodes or custom nodes
if op.doc:
# only set if a comment is attached to the node
- assert node_template["metadata"]["annotations"].get("elyra/node-user-doc") == op.doc
+ assert pod_metadata["annotations"]["elyra/node-user-doc"] == op.doc
# Verify Kubernetes labels and annotations that Elyra attaches to pods that
# execute generic nodes
- assert node_template["metadata"]["annotations"]["elyra/node-file-name"] == op.filename
+ assert pod_metadata["annotations"]["elyra/node-file-name"] == op.filename
if pipeline.source:
- assert node_template["metadata"]["annotations"]["elyra/pipeline-source"] == pipeline.source
- assert node_template["metadata"]["labels"]["elyra/node-name"] == sanitize_label_value(op.name)
- assert node_template["metadata"]["labels"]["elyra/node-type"] == sanitize_label_value("notebook-script")
- assert node_template["metadata"]["labels"]["elyra/pipeline-name"] == sanitize_label_value(pipeline.name)
- assert node_template["metadata"]["labels"]["elyra/pipeline-version"] == sanitize_label_value(pipeline_version)
- assert node_template["metadata"]["labels"]["elyra/experiment-name"] == sanitize_label_value(experiment_name)
+ assert pod_metadata["annotations"]["elyra/pipeline-source"] == pipeline.source
+ assert pod_metadata["labels"]["elyra/node-name"] == sanitize_label_value(op.name)
+ assert pod_metadata["labels"]["elyra/node-type"] == sanitize_label_value("notebook-script")
+ assert pod_metadata["labels"]["elyra/pipeline-name"] == sanitize_label_value(pipeline.name)
+ assert pod_metadata["labels"]["elyra/pipeline-version"] == sanitize_label_value(pipeline_version)
+ assert pod_metadata["labels"]["elyra/experiment-name"] == sanitize_label_value(experiment_name)
# Verify environment variables that Elyra attaches to pods that
# execute generic nodes. All values are hard-coded in the template, with the
@@ -828,20 +747,6 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_one_generic_node_pipeline_te
else:
assert env_var["value"] == runtime_config.metadata["cos_password"]
- # Verify that the mlpipeline specific outputs are declared
- assert node_template.get("outputs") is not None, node_template
- assert node_template["outputs"]["artifacts"] is not None, node_template["container"]["outputs"]
- assert node_template["outputs"]["artifacts"][0]["name"] == "mlpipeline-metrics"
- assert (
- node_template["outputs"]["artifacts"][0]["path"]
- == (Path(KfpPipelineProcessor.WCD) / "mlpipeline-metrics.json").as_posix()
- )
- assert node_template["outputs"]["artifacts"][1]["name"] == "mlpipeline-ui-metadata"
- assert (
- node_template["outputs"]["artifacts"][1]["path"]
- == (Path(KfpPipelineProcessor.WCD) / "mlpipeline-ui-metadata.json").as_posix()
- )
-
@pytest.mark.parametrize(
"metadata_dependencies",
@@ -928,29 +833,34 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_one_generic_node_pipeline_te
# Load generated Argo workflow
with open(compiled_argo_output_file_name) as f:
- argo_spec = yaml.safe_load(f.read())
+ spec_docs = list(yaml.safe_load_all(f.read()))
+
+ assert len(spec_docs) == 2
+ components = spec_docs[0]
- # verify that this is an argo specification
- assert "argoproj.io" in argo_spec["apiVersion"]
+ assert components["pipelineInfo"]["name"] == pipeline.name
+ assert components["pipelineInfo"]["description"] == pipeline.description
# There should be two templates, one for the DAG and one for the generic node.
# Locate the one for the generic node and inspect its properties.
- assert len(argo_spec["spec"]["templates"]) == 2
- if argo_spec["spec"]["templates"][0]["name"] == argo_spec["spec"]["entrypoint"]:
- node_template = argo_spec["spec"]["templates"][1]
- else:
- node_template = argo_spec["spec"]["templates"][0]
+ assert components["root"]["dag"]
+ assert len(components["components"]) == 1
+ executors = components["deploymentSpec"]["executors"]
+ assert len(executors) == 1
op = list(pipeline.operations.values())[0]
- if op.gpu or op.cpu or op.memory:
- assert node_template["container"].get("resources") is not None
+ node_template = components["deploymentSpec"]["executors"]["exec-run-a-file"]
+
+ if op.gpu or op.cpu or op.memory or op.cpu_limit or op.memory_limit:
+ assert node_template["container"]["resources"]
if op.gpu:
- assert node_template["container"]["resources"]["limits"]["nvidia.com/gpu"] == str(op.gpu)
+ assert node_template["container"]["resources"]["accelerator"]["type"] == "nvidia.com/gpu"
+ assert node_template["container"]["resources"]["accelerator"]["count"] == str(op.gpu)
if op.cpu:
- assert node_template["container"]["resources"]["requests"]["cpu"] == str(op.cpu)
+ assert node_template["container"]["resources"]["cpuRequest"] == op.cpu
if op.memory:
- assert node_template["container"]["resources"]["requests"]["memory"] == f"{op.memory}G"
+ assert node_template["container"]["resources"]["memoryRequest"] == op.memory
@pytest.fixture(autouse=False)
@@ -969,156 +879,6 @@ def enable_and_disable_crio(request):
del os.environ["CRIO_RUNTIME"]
-@pytest.mark.parametrize("enable_and_disable_crio", [False, True], indirect=True)
-@pytest.mark.parametrize(
- "metadata_dependencies",
- [
- {
- "pipeline_file": Path(__file__).parent
- / ".."
- / "resources"
- / "test_pipelines"
- / "kfp"
- / "kfp-one-node-generic.pipeline",
- "workflow_engine": WorkflowEngineType.ARGO,
- },
- ],
- indirect=True,
-)
-def test_generate_pipeline_dsl_compile_pipeline_dsl_generic_component_crio(
- monkeypatch, processor: KfpPipelineProcessor, metadata_dependencies: Dict[str, Any], tmpdir, enable_and_disable_crio
-):
- """
- This test validates that the output of _generate_pipeline_dsl and _compile_pipeline_dsl
- yields the expected results for a generic node when the CRIO_RUNTIME environment variable
- is set to a valid string representation of the boolean value True (/true/i).
- Test assumptions:
- - Enabling CRIO_RUNTIME has the same effect for all supported workflow engines
- - The test pipeline contains at least one generic node
-
- With CRIO_RUNTIME enabled, the compiled output must include the following properties:
- - in spec.templates[].volumes:
- - emptyDir: {medium: '', sizeLimit: 20Gi}
- name: workspace
- """
- crio_runtime_enabled = os.environ.get("CRIO_RUNTIME", "").lower() == "true"
-
- # Obtain artifacts from metadata_dependencies fixture
- test_pipeline_file = metadata_dependencies["pipeline_file"]
- pipeline = metadata_dependencies["pipeline_object"]
- assert pipeline is not None
- runtime_config = metadata_dependencies["runtime_config"]
- assert runtime_config is not None
-
- workflow_engine = WorkflowEngineType.get_instance_by_value(runtime_config.metadata["engine"])
-
- # Mock calls that require access to object storage, because their side effects
- # have no bearing on the outcome of this test.
- monkeypatch.setattr(processor, "_upload_dependencies_to_object_store", lambda w, x, y, prefix: True)
- monkeypatch.setattr(processor, "_verify_cos_connectivity", lambda x: True)
-
- # Mock pipeline to not include any parameters
- monkeypatch.setattr(pipeline, "_pipeline_parameters", ElyraPropertyList([]))
-
- # Test begins here
-
- compiled_output_file = Path(tmpdir) / test_pipeline_file.with_suffix(".yaml").name
- compiled_output_file_name = str(compiled_output_file.absolute())
-
- # generate Python DSL for the specified workflow engine
- pipeline_version = f"{pipeline.name}-test-0"
- pipeline_instance_id = f"{pipeline.name}-{datetime.now().strftime('%m%d%H%M%S')}"
- experiment_name = f"{pipeline.name}-test-0"
-
- generated_dsl = processor._generate_pipeline_dsl(
- pipeline=pipeline,
- pipeline_name=pipeline.name,
- workflow_engine=workflow_engine,
- pipeline_version=pipeline_version,
- pipeline_instance_id=pipeline_instance_id,
- experiment_name=experiment_name,
- )
-
- # Compile the DSL
- processor._compile_pipeline_dsl(
- dsl=generated_dsl,
- workflow_engine=workflow_engine,
- output_file=compiled_output_file_name,
- pipeline_conf=None,
- )
-
- # Load compiled workflow
- with open(compiled_output_file_name) as f:
- compiled_spec = yaml.safe_load(f.read())
-
- # There should be multiple templates, one for the DAG and one for every generic node.
- assert len(compiled_spec["spec"]["templates"]) >= 2
- if crio_runtime_enabled:
- for template in compiled_spec["spec"]["templates"]:
- if template["name"] == compiled_spec["spec"]["entrypoint"]:
- continue
- # Check volume definition
- assert template.get("volumes") is not None, template
- entry_found = False
- for volume_entry in template["volumes"]:
- if volume_entry["name"] != CRIO_VOL_DEF_NAME:
- continue
- assert (
- volume_entry.get("emptyDir") is not None
- ), f"Unexpected volume entry '{CRIO_VOL_DEF_NAME}': {volume_entry} "
- assert volume_entry["emptyDir"]["sizeLimit"] == CRIO_VOL_DEF_SIZE
- assert volume_entry["emptyDir"]["medium"] == CRIO_VOL_DEF_MEDIUM
- entry_found = True
- assert entry_found, f"Missing volume entry '{CRIO_VOL_DEF_NAME}' for CRI-O in {template['volumes']}"
- # Check volume mount definition
- assert template["container"].get("volumeMounts") is not None, template["container"]
- for volumemount_entry in template["container"]["volumeMounts"]:
- entry_found = False
- if volumemount_entry["name"] != CRIO_VOL_DEF_NAME:
- continue
- assert volumemount_entry["mountPath"] == CRIO_VOL_MOUNT_PATH
- entry_found = True
- break
- assert (
- entry_found
- ), f"Missing volume mount entry '{CRIO_VOL_DEF_NAME}' for CRI-O in {template['container']['volumeMounts']}"
- # Check PYTHONPATH environment variable (python_user_lib_path)
- assert template["container"].get("env") is not None, template["container"]
- for env_entry in template["container"]["env"]:
- entry_found = False
- if env_entry["name"] != "PYTHONPATH":
- continue
- assert env_entry["value"] == CRIO_VOL_PYTHON_PATH
- entry_found = True
- break
- assert entry_found, f"Missing env variable entry 'PYTHONPATH' for CRI-O in {template['container']['env']}"
- # Check the container command argument list
- assert len(template["container"]["args"]) == 1
- assert f"mkdir -p {CRIO_VOL_WORKDIR_PATH}" in template["container"]["args"][0]
- assert f"--target={CRIO_VOL_PYTHON_PATH}" in template["container"]["args"][0]
- assert f"--user-volume-path '{CRIO_VOL_PYTHON_PATH}' " in template["container"]["args"][0]
- else:
- for template in compiled_spec["spec"]["templates"]:
- if template["name"] == compiled_spec["spec"]["entrypoint"]:
- continue
- # Check if a volume was defined
- for volume_entry in template.get("volumes", []):
- if volume_entry["name"] == CRIO_VOL_DEF_NAME:
- # if a volume with the 'reserved' name exist there could be a problem
- assert volume_entry.get("emptyDir") is None
- # Check volume mount definition
- for volumemount_entry in template["container"].get("volumeMounts", []):
- if volumemount_entry["name"] == CRIO_VOL_DEF_NAME:
- assert volumemount_entry["mountPath"] != CRIO_VOL_MOUNT_PATH
- # Check PYTHONPATH environment variable
- for env_entry in template["container"].get("env", []):
- assert env_entry["name"] != "PYTHONPATH"
- # Check the container command argument list
- assert "mkdir -p ./jupyter-work-dir" in template["container"]["args"][0]
- assert f"--target={CRIO_VOL_PYTHON_PATH}" not in template["container"]["args"][0]
- assert "--user-volume-path" not in template["container"]["args"][0]
-
-
@pytest.mark.parametrize(
"metadata_dependencies",
[
@@ -1134,6 +894,10 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_generic_component_crio(
],
indirect=True,
)
+@pytest.mark.skip(
+ reason="This test is not compatible with KFP v2 as the generated YAML is ignoring \
+ attributes from the source pipeline file"
+)
def test_generate_pipeline_dsl_compile_pipeline_dsl_optional_elyra_properties(
monkeypatch, processor: KfpPipelineProcessor, metadata_dependencies: Dict[str, Any], tmpdir
):
@@ -1174,6 +938,8 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_optional_elyra_properties(
compiled_output_file = Path(tmpdir) / test_pipeline_file.with_suffix(".yaml").name
compiled_output_file_name = str(compiled_output_file.absolute())
+ print(f">>>> compiled_output_file_name: {compiled_output_file_name}")
+
# generate Python DSL
pipeline_version = f"{pipeline.name}-0815"
pipeline_instance_id = f"{pipeline.name}-{datetime.now().strftime('%m%d%H%M%S')}"
@@ -1197,76 +963,65 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_optional_elyra_properties(
# Load compiled output
with open(compiled_output_file_name) as fh:
- compiled_spec = yaml.safe_load(fh.read())
+ spec_docs = list(yaml.safe_load_all(fh.read()))
- # There should be two templates, one for the DAG and one for the generic node.
- # Locate the one for the generic node and inspect its properties.
- assert len(compiled_spec["spec"]["templates"]) == 2
- if compiled_spec["spec"]["templates"][0]["name"] == compiled_spec["spec"]["entrypoint"]:
- node_template = compiled_spec["spec"]["templates"][1]
- else:
- node_template = compiled_spec["spec"]["templates"][0]
+ assert len(spec_docs) == 2
+ assert "components" in spec_docs[0]
+ assert "platforms" in spec_docs[1]
#
# validate data volumes, if applicable
expected_volume_mounts = op.elyra_props.get(MOUNTED_VOLUMES)
if len(expected_volume_mounts) > 0:
# There must be one or more 'volumeMounts' entry and one or more 'volumes' entry
- assert node_template["container"].get("volumeMounts") is not None, node_template["container"]
- assert node_template.get("volumes") is not None, compiled_spec["spec"]
+ assert (
+ spec_docs[1]["platforms"]["kubernetes"]["deploymentSpec"]["executors"]["exec-run-a-file"].get("pvcMount")
+ is not None
+ ), spec_docs[1]["platforms"]["kubernetes"]
+ pvc_mounts = spec_docs[1]["platforms"]["kubernetes"]["deploymentSpec"]["executors"]["exec-run-a-file"][
+ "pvcMount"
+ ]
- assert len(node_template["container"]["volumeMounts"]) >= len(expected_volume_mounts)
+ assert len(pvc_mounts) >= len(expected_volume_mounts)
for volume_mount in expected_volume_mounts:
- for volumemount_entry in node_template["container"]["volumeMounts"]:
+ for volumemount_entry in pvc_mounts:
entry_found = False
if volumemount_entry["mountPath"] == volume_mount.path:
- assert volumemount_entry["name"] == volume_mount.pvc_name
- assert volumemount_entry.get("subPath", None) == volume_mount.sub_path
- assert volumemount_entry.get("readOnly", None) == volume_mount.read_only
- entry_found = True
- break
- assert (
- entry_found
- ), f"Cannot find volume mount entry '{volume_mount.path}' in {node_template['container']['volumeMounts']}"
- for volume_entry in node_template["volumes"]:
- entry_found = False
- if volume_entry["name"] == volume_mount.pvc_name:
- assert volume_entry["persistentVolumeClaim"]["claimName"] == volume_mount.pvc_name
+ assert volumemount_entry["constant"] == volume_mount.pvc_name
+ # the following attributes are currently ignored in KFP v2.
+ # Once they are implemented, the code below needs to be updated accordingly.
+ # Reference: https://github.com/kubeflow/pipelines/blob/master/
+ # kubernetes_platform/proto/kubernetes_executor_config.proto#L84
+ #
+ # assert volumemount_entry.get("subPath", None) == volume_mount.sub_path
+ # assert volumemount_entry.get("readOnly", False) == volume_mount.read_only
entry_found = True
break
- assert (
- entry_found
- ), f"Cannot find volume entry '{volume_mount.path}' in {node_template['container']['volumeMounts']}"
+ assert entry_found, f"Cannot find volume mount entry '{volume_mount.path}' in {pvc_mounts}"
#
# validate custom shared memory size, if applicable
custom_shared_mem_size = op.elyra_props.get(KUBERNETES_SHARED_MEM_SIZE)
if custom_shared_mem_size:
# There must be one 'volumeMounts' entry and one 'volumes' entry
- assert node_template["container"].get("volumeMounts") is not None, node_template["container"]
- assert node_template.get("volumes") is not None, compiled_spec["spec"]
- for volumemount_entry in node_template["container"]["volumeMounts"]:
+ assert (
+ spec_docs[1]["platforms"]["kubernetes"]["deploymentSpec"]["executors"]["exec-run-a-file"].get("pvcMount")
+ is not None
+ ), spec_docs[1]["platforms"]["kubernetes"]
+ pvc_mounts = spec_docs[1]["platforms"]["kubernetes"]["deploymentSpec"]["executors"]["exec-run-a-file"][
+ "pvcMount"
+ ]
+
+ for volumemount_entry in pvc_mounts:
entry_found = False
if volumemount_entry["mountPath"] == "/dev/shm":
assert volumemount_entry["name"] == "shm"
entry_found = True
break
- assert (
- entry_found
- ), "Missing volume mount entry for shared memory size in {node_template['container']['volumeMounts']}"
- for volume_entry in node_template["volumes"]:
- entry_found = False
- if volume_entry["name"] == "shm":
- assert volume_entry["emptyDir"]["medium"] == "Memory"
- assert (
- volume_entry["emptyDir"]["sizeLimit"]
- == f"{custom_shared_mem_size.size}{custom_shared_mem_size.units}"
- )
- entry_found = True
- break
- assert (
- entry_found
- ), f"Missing volume entry for shm size '{volume_mount.path}' in {node_template['container']['volumeMounts']}"
+ assert entry_found, "Missing volume mount entry for shared memory size in {pvc_mounts}"
+
+ """
+ IMPORTANT: TODO: The following code needs to be updated to the KFP v2 once the feature is implemented.
#
# validate Kubernetes secrets, if applicable
@@ -1332,6 +1087,7 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_optional_elyra_properties(
f"in {node_template['tolerations']}"
)
assert entry_found, not_found_msg
+ """
@pytest.mark.parametrize(
@@ -1407,20 +1163,23 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_generic_components_data_exch
# Load compiled output
with open(compiled_output_file_name) as fh:
- compiled_spec = yaml.safe_load(fh.read())
+ compiled_spec_docs = list(yaml.safe_load_all(fh.read()))
+
+ assert len(compiled_spec_docs) == 2
+ assert "components" in compiled_spec_docs[0]
+ assert "platforms" in compiled_spec_docs[1]
# There should be at least four templates, one for the DAG and three
# for generic nodes. Each template spec for generic nodes is named
# "run-a-file[-index]". The "-index" is added by the compiler to
# guarantee uniqueness.
- assert len(compiled_spec["spec"]["templates"]) >= 3
+ executors = compiled_spec_docs[0]["deploymentSpec"]["executors"]
+ assert len(executors) >= 3
template_specs = {}
- for node_template in compiled_spec["spec"]["templates"]:
- if node_template["name"] == compiled_spec["spec"]["entrypoint"] or not node_template["name"].startswith(
- "run-a-file"
- ):
+ for node_template_name, node_template in executors.items():
+ if node_template_name == executors or not node_template_name.startswith("exec-run-a-file"):
continue
- template_specs[node_template["name"]] = node_template
+ template_specs[node_template_name] = node_template
# Iterate through sorted operations and verify that their inputs
# and outputs are properly represented in their respective template
@@ -1431,9 +1190,9 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_generic_components_data_exch
# ignore custom nodes
continue
if template_index == 1:
- template_name = "run-a-file"
+ template_name = "exec-run-a-file"
else:
- template_name = f"run-a-file-{template_index}"
+ template_name = f"exec-run-a-file-{template_index}"
template_index = template_index + 1
# compare outputs
if len(op.outputs) > 0:
@@ -1473,6 +1232,9 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_generic_components_data_exch
],
indirect=True,
)
+@pytest.mark.skip(
+ reason="This test is not compatible with KFP v2: There is no `imagePullSecrets` in the generated YAML to be verified." # noqa: E501
+)
def test_generate_pipeline_dsl_compile_pipeline_dsl_generic_components_pipeline_conf(
monkeypatch, processor: KfpPipelineProcessor, metadata_dependencies: Dict[str, Any], tmpdir
):
@@ -1627,24 +1389,20 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_generic_components_with_para
# Load compiled workflow
with open(compiled_output_file_name) as f:
- compiled_spec = yaml.safe_load(f.read())
+ compiled_spec_docs = list(yaml.safe_load_all(f.read()))
+
+ assert len(compiled_spec_docs) == 2
+ assert "components" in compiled_spec_docs[0]
+ assert "platforms" in compiled_spec_docs[1]
# Test parameters appear as expected
- yaml_pipeline_params = compiled_spec["spec"]["arguments"]["parameters"]
+ yaml_pipeline_params = compiled_spec_docs[0]["root"]["inputDefinitions"]["parameters"]
# Only two parameters are referenced by a node in the pipeline, so only 2 should be present in YAML
assert len(yaml_pipeline_params) == 2
# Assert params defined in YAML correspond to those defined by the Pipeline object
- for param_from_yaml in yaml_pipeline_params:
- param_name, param_value = param_from_yaml.get("name"), param_from_yaml.get("value")
- assert any(param.name == param_name and str(param.value) == param_value for param in pipeline.parameters)
-
- yaml_node_params = compiled_spec["spec"]["templates"][1]["inputs"]["parameters"]
- # Only two parameters are referenced by this node, so only 2 should be present as inputs
- assert len(yaml_node_params) == 2
- # Assert params defined in YAML correspond to those defined by the Pipeline object
- for param_from_yaml in yaml_node_params:
- param_name = param_from_yaml.get("name")
- assert any(param.name == param_name for param in pipeline.parameters)
+ for param_name, param_info in yaml_pipeline_params.items():
+ param_value = param_info["defaultValue"]
+ assert any(param.name == param_name and param.value == param_value for param in pipeline.parameters)
def test_generate_pipeline_dsl_compile_pipeline_dsl_custom_components_with_parameters(
@@ -1772,21 +1530,15 @@ def test_generate_pipeline_dsl_compile_pipeline_dsl_custom_components_with_param
print(compiled_spec)
# Test parameters appear as expected
- yaml_pipeline_params = compiled_spec["spec"]["arguments"]["parameters"]
+ yaml_pipeline_params = compiled_spec["root"]["inputDefinitions"]["parameters"]
# Only two parameters are referenced by a node in the pipeline, so only 1 should be present in YAML
assert len(yaml_pipeline_params) == 1
# Assert params defined in YAML correspond to those defined by the Pipeline object
- for param_from_yaml in yaml_pipeline_params:
- param_name, param_value = param_from_yaml.get("name"), param_from_yaml.get("value")
- assert any(param.name == param_name and str(param.value) == param_value for param in pipeline.parameters)
-
- yaml_node_params = compiled_spec["spec"]["templates"][0]["inputs"]["parameters"]
- # Only two parameters are referenced by this node, so only 1 should be present as input
- assert len(yaml_node_params) == 1
- # Assert params defined in YAML correspond to those defined by the Pipeline object
- for param_from_yaml in yaml_node_params:
- param_name = param_from_yaml.get("name")
- assert any(param.name == param_name for param in pipeline.parameters)
+ for param_from_yaml_key, param_from_yaml_value in yaml_pipeline_params.items():
+ assert any(
+ param.name == param_from_yaml_key and str(param.value) == param_from_yaml_value["defaultValue"]
+ for param in pipeline.parameters
+ )
def test_kfp_invalid_pipeline_parameter_type():
diff --git a/elyra/tests/pipeline/resources/components/download_data.yaml b/elyra/tests/pipeline/resources/components/download_data.yaml
index c8e8aab1b..74ccd263b 100644
--- a/elyra/tests/pipeline/resources/components/download_data.yaml
+++ b/elyra/tests/pipeline/resources/components/download_data.yaml
@@ -14,8 +14,8 @@
#
name: Download data
inputs:
-- {name: Url, type: URI}
-- {name: curl options, type: string, default: '--location', description: 'Additional options given to the curl program. See https://curl.haxx.se/docs/manpage.html'}
+- {name: Url, type: String}
+- {name: curl options, type: String, default: '--location', description: 'Additional options given to the curl program. See https://curl.haxx.se/docs/manpage.html'}
outputs:
- {name: Data}
metadata:
diff --git a/elyra/tests/pipeline/resources/components/filter_text.yaml b/elyra/tests/pipeline/resources/components/filter_text.yaml
index 815d6711d..22259806f 100644
--- a/elyra/tests/pipeline/resources/components/filter_text.yaml
+++ b/elyra/tests/pipeline/resources/components/filter_text.yaml
@@ -12,17 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-# Component source location: https://raw.githubusercontent.com/kubeflow/pipelines/master/components/sample/Shell_script/component.yaml
+# Component source location: https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/sample/Shell_script/component.yaml
# Component details: Takes a text file and a regex pattern filter to produce a filtered text file
-name: Filter text
+name: Filter text using shell and grep
inputs:
-- {name: Text, optional: false, description: 'Path to file to be filtered'}
-- {name: Pattern, optional: true, default: '.*', description: 'Regex pattern'}
+- {name: Text, type: String}
+- {name: Pattern, default: '.*', type: String}
outputs:
-- {name: Filtered text}
+- {name: Filtered text, type: String}
metadata:
annotations:
author: Alexey Volkov
+ canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/sample/Shell_script/component.yaml'
implementation:
container:
image: alpine
@@ -34,8 +35,5 @@ implementation:
pattern=$1
filtered_text_path=$2
mkdir -p "$(dirname "$filtered_text_path")"
-
+
grep "$pattern" < "$text_path" > "$filtered_text_path"
- - {inputPath: Text}
- - {inputValue: Pattern}
- - {outputPath: Filtered text}
diff --git a/elyra/tests/pipeline/resources/validation_pipelines/kf_inputpath_parameter.pipeline b/elyra/tests/pipeline/resources/validation_pipelines/kf_inputpath_parameter.pipeline
index c9b3982dc..2dbde813d 100644
--- a/elyra/tests/pipeline/resources/validation_pipelines/kf_inputpath_parameter.pipeline
+++ b/elyra/tests/pipeline/resources/validation_pipelines/kf_inputpath_parameter.pipeline
@@ -16,7 +16,7 @@
"component_parameters": {
"url": {
"widget": "string",
- "value": "https://raw.githubusercontent.com/kubeflow/pipelines/93fc34474bf989998cf19445149aca2847eee763/components/notebooks/samples/test_notebook.ipynb"
+ "value": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/notebooks/samples/test_notebook.ipynb"
},
"curl_options": {
"widget": "string",
@@ -25,7 +25,7 @@
"output_data": ""
},
"label": "",
- "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/1.6.0/components/web/Download/component.yaml",
+ "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/web/Download/component-sdk-v2.yaml",
"ui_data": {
"label": "Download data",
"image": "/static/elyra/kubeflow.svg",
@@ -156,7 +156,7 @@
"output_hash": ""
},
"label": "",
- "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/1.6.0/components/basics/Calculate_hash/component.yaml",
+ "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/basics/Calculate_hash/component.yaml",
"ui_data": {
"label": "Calculate data hash",
"image": "/static/elyra/kubeflow.svg",
diff --git a/elyra/tests/pipeline/resources/validation_pipelines/kf_invalid_inputpath_missing_connection.pipeline b/elyra/tests/pipeline/resources/validation_pipelines/kf_invalid_inputpath_missing_connection.pipeline
index 3082695ff..a320599d4 100644
--- a/elyra/tests/pipeline/resources/validation_pipelines/kf_invalid_inputpath_missing_connection.pipeline
+++ b/elyra/tests/pipeline/resources/validation_pipelines/kf_invalid_inputpath_missing_connection.pipeline
@@ -91,7 +91,7 @@
}
},
"label": "",
- "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/1.6.0/components/web/Download/component.yaml",
+ "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/web/Download/component-sdk-v2.yaml",
"ui_data": {
"label": "Download data",
"image": "/static/elyra/kubeflow.svg",
@@ -148,7 +148,7 @@
}
},
"label": "",
- "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/1.6.0/components/basics/Calculate_hash/component.yaml",
+ "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/basics/Calculate_hash/component.yaml",
"ui_data": {
"label": "Calculate data hash",
"image": "/static/elyra/kubeflow.svg",
diff --git a/elyra/tests/pipeline/resources/validation_pipelines/kf_invalid_inputpath_parameter.pipeline b/elyra/tests/pipeline/resources/validation_pipelines/kf_invalid_inputpath_parameter.pipeline
index 2a7f1bde2..90d431cc7 100644
--- a/elyra/tests/pipeline/resources/validation_pipelines/kf_invalid_inputpath_parameter.pipeline
+++ b/elyra/tests/pipeline/resources/validation_pipelines/kf_invalid_inputpath_parameter.pipeline
@@ -16,7 +16,7 @@
"component_parameters": {
"url": {
"widget": "string",
- "value": "https://raw.githubusercontent.com/kubeflow/pipelines/93fc34474bf989998cf19445149aca2847eee763/components/notebooks/samples/test_notebook.ipynb"
+ "value": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/notebooks/samples/test_notebook.ipynb"
},
"curl_options": {
"widget": "string",
@@ -25,7 +25,7 @@
"output_data": ""
},
"label": "",
- "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/1.6.0/components/web/Download/component.yaml",
+ "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/web/Download/component-sdk-v2.yaml",
"ui_data": {
"label": "Download data",
"image": "/static/elyra/kubeflow.svg",
@@ -155,7 +155,7 @@
"output_hash": ""
},
"label": "",
- "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/1.6.0/components/basics/Calculate_hash/component.yaml",
+ "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/basics/Calculate_hash/component.yaml",
"ui_data": {
"label": "Calculate data hash",
"image": "/static/elyra/kubeflow.svg",
diff --git a/elyra/tests/pipeline/resources/validation_pipelines/kf_supernode_invalid_single_cycle.pipeline b/elyra/tests/pipeline/resources/validation_pipelines/kf_supernode_invalid_single_cycle.pipeline
index 08b0febac..712b26176 100644
--- a/elyra/tests/pipeline/resources/validation_pipelines/kf_supernode_invalid_single_cycle.pipeline
+++ b/elyra/tests/pipeline/resources/validation_pipelines/kf_supernode_invalid_single_cycle.pipeline
@@ -19,7 +19,7 @@
"output_data": ""
},
"label": "",
- "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/1.6.0/components/web/Download/component.yaml",
+ "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/web/Download/component-sdk-v2.yaml",
"ui_data": {
"label": "Download data",
"image": "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20276.93%20274.55%22%3E%3Cg%20id%3D%22Layer_2%22%20data-name%3D%22Layer%202%22%3E%3Cg%20id%3D%22Layer_1-2%22%20data-name%3D%22Layer%201%22%3E%3Cpath%20d%3D%22M95.9%2C62.15%2C100%2C164.25l73.75-94.12a6.79%2C6.79%2C0%2C0%2C1%2C9.6-1.11l46%2C36.92-15-65.61Z%22%20fill%3D%22%234279f4%22%2F%3E%3Cpolygon%20points%3D%22102.55%20182.98%20167.97%20182.98%20127.8%20150.75%20102.55%20182.98%22%20fill%3D%22%230028aa%22%2F%3E%3Cpolygon%20points%3D%22180.18%2083.92%20136.18%20140.06%20183.06%20177.67%20227.53%20121.91%20180.18%2083.92%22%20fill%3D%22%23014bd1%22%2F%3E%3Cpolygon%20points%3D%2283.56%2052.3%2083.57%2052.29%20122.26%203.77%2059.87%2033.82%2044.46%20101.33%2083.56%2052.3%22%20fill%3D%22%23bedcff%22%2F%3E%3Cpolygon%20points%3D%2245.32%20122.05%2086.76%20174.01%2082.81%2075.03%2045.32%20122.05%22%20fill%3D%22%236ca1ff%22%2F%3E%3Cpolygon%20points%3D%22202.31%2028.73%20142.65%200%20105.52%2046.56%20202.31%2028.73%22%20fill%3D%22%23a1c3ff%22%2F%3E%3Cpath%20d%3D%22M1.6%2C272V227.22H7.34v23.41l20.48-23.41h6.4l-17.39%2C19.7%2C19%2C25.07H29.1l-15.92-20.8-5.84%2C6.65V272Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M41.62%2C262.21V240h5.43v22.39a4.67%2C4.67%2C0%2C0%2C0%2C2.35%2C4.19%2C11%2C11%2C0%2C0%2C0%2C11%2C0%2C4.69%2C4.69%2C0%2C0%2C0%2C2.33-4.19V240h5.43v22.19a9.08%2C9.08%2C0%2C0%2C1-4.1%2C7.87%2C16.2%2C16.2%2C0%2C0%2C1-18.37%2C0A9.07%2C9.07%2C0%2C0%2C1%2C41.62%2C262.21Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M77.46%2C272V224h5.43v16.81a29.29%2C29.29%2C0%2C0%2C1%2C9.32-1.73%2C13.1%2C13.1%2C0%2C0%2C1%2C6.2%2C1.41%2C10.71%2C10.71%2C0%2C0%2C1%2C4.18%2C3.74%2C18.07%2C18.07%2C0%2C0%2C1%2C2.23%2C5.06%2C21.26%2C21.26%2C0%2C0%2C1%2C.73%2C5.58q0%2C8.43-4.38%2C12.79T87.35%2C272Zm5.43-4.87h4.55q6.77%2C0%2C9.72-2.95t3-9.51a14.21%2C14.21%2C0%2C0%2C0-2-7.52%2C6.55%2C6.55%2C0%2C0%2C0-6-3.22%2C24.73%2C24.73%2C0%2C0%2C0-9.25%2C1.54Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M112.36%2C255.94q0-7.71%2C4.09-12.3a13.75%2C13.75%2C0%2C0%2C1%2C10.8-4.59q13.35%2C0%2C13.36%2C18.86H117.79a12.3%2C12.3%2C0%2C0%2C0%2C2.9%2C7.07q2.59%2C3.11%2C7.9%2C3.1a24.92%2C24.92%2C0%2C0%2C0%2C10.55-2v5a27.74%2C27.74%2C0%2C0%2C1-9.86%2C1.87%2C19.83%2C19.83%2C0%2C0%2C1-7.7-1.37%2C13.31%2C13.31%2C0%2C0%2C1-5.28-3.76%2C16.21%2C16.21%2C0%2C0%2C1-3-5.38A20.84%2C20.84%2C0%2C0%2C1%2C112.36%2C255.94Zm5.62-2.12h17.26a14.91%2C14.91%2C0%2C0%2C0-2.37-7.12%2C6.44%2C6.44%2C0%2C0%2C0-5.62-2.78%2C8.2%2C8.2%2C0%2C0%2C0-6.21%2C2.72A12.07%2C12.07%2C0%2C0%2C0%2C118%2C253.82Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M147.32%2C244.89V240h5v-7.59a8.14%2C8.14%2C0%2C0%2C1%2C2.31-6.05%2C7.79%2C7.79%2C0%2C0%2C1%2C5.69-2.28h7.86V229h-5c-2.21%2C0-3.67.45-4.37%2C1.34s-1.06%2C2.55-1.06%2C5V240h8.46v4.87h-8.46V272h-5.44v-27.1Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M175.26%2C272V224h5.43v48Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M194.41%2C268.05a17.86%2C17.86%2C0%2C1%2C1%2C12.33%2C4.9A16.57%2C16.57%2C0%2C0%2C1%2C194.41%2C268.05Zm3.84-20.65a13.16%2C13.16%2C0%2C0%2C0%2C0%2C17.2%2C12.07%2C12.07%2C0%2C0%2C0%2C17%2C0%2C13.09%2C13.09%2C0%2C0%2C0%2C0-17.2%2C12.07%2C12.07%2C0%2C0%2C0-17%2C0Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M228.45%2C240h5.75l7.3%2C25.32L248.93%2C240h5.36l7.34%2C25.34L269%2C240h5.74L264.7%2C272h-6.12l-6.83-24.58L245%2C272h-6.47Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E",
@@ -277,7 +277,7 @@
}
},
"label": "",
- "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/1.6.0/components/basics/Calculate_hash/component.yaml",
+ "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/basics/Calculate_hash/component.yaml",
"ui_data": {
"label": "Calculate data hash",
"image": "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20276.93%20274.55%22%3E%3Cg%20id%3D%22Layer_2%22%20data-name%3D%22Layer%202%22%3E%3Cg%20id%3D%22Layer_1-2%22%20data-name%3D%22Layer%201%22%3E%3Cpath%20d%3D%22M95.9%2C62.15%2C100%2C164.25l73.75-94.12a6.79%2C6.79%2C0%2C0%2C1%2C9.6-1.11l46%2C36.92-15-65.61Z%22%20fill%3D%22%234279f4%22%2F%3E%3Cpolygon%20points%3D%22102.55%20182.98%20167.97%20182.98%20127.8%20150.75%20102.55%20182.98%22%20fill%3D%22%230028aa%22%2F%3E%3Cpolygon%20points%3D%22180.18%2083.92%20136.18%20140.06%20183.06%20177.67%20227.53%20121.91%20180.18%2083.92%22%20fill%3D%22%23014bd1%22%2F%3E%3Cpolygon%20points%3D%2283.56%2052.3%2083.57%2052.29%20122.26%203.77%2059.87%2033.82%2044.46%20101.33%2083.56%2052.3%22%20fill%3D%22%23bedcff%22%2F%3E%3Cpolygon%20points%3D%2245.32%20122.05%2086.76%20174.01%2082.81%2075.03%2045.32%20122.05%22%20fill%3D%22%236ca1ff%22%2F%3E%3Cpolygon%20points%3D%22202.31%2028.73%20142.65%200%20105.52%2046.56%20202.31%2028.73%22%20fill%3D%22%23a1c3ff%22%2F%3E%3Cpath%20d%3D%22M1.6%2C272V227.22H7.34v23.41l20.48-23.41h6.4l-17.39%2C19.7%2C19%2C25.07H29.1l-15.92-20.8-5.84%2C6.65V272Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M41.62%2C262.21V240h5.43v22.39a4.67%2C4.67%2C0%2C0%2C0%2C2.35%2C4.19%2C11%2C11%2C0%2C0%2C0%2C11%2C0%2C4.69%2C4.69%2C0%2C0%2C0%2C2.33-4.19V240h5.43v22.19a9.08%2C9.08%2C0%2C0%2C1-4.1%2C7.87%2C16.2%2C16.2%2C0%2C0%2C1-18.37%2C0A9.07%2C9.07%2C0%2C0%2C1%2C41.62%2C262.21Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M77.46%2C272V224h5.43v16.81a29.29%2C29.29%2C0%2C0%2C1%2C9.32-1.73%2C13.1%2C13.1%2C0%2C0%2C1%2C6.2%2C1.41%2C10.71%2C10.71%2C0%2C0%2C1%2C4.18%2C3.74%2C18.07%2C18.07%2C0%2C0%2C1%2C2.23%2C5.06%2C21.26%2C21.26%2C0%2C0%2C1%2C.73%2C5.58q0%2C8.43-4.38%2C12.79T87.35%2C272Zm5.43-4.87h4.55q6.77%2C0%2C9.72-2.95t3-9.51a14.21%2C14.21%2C0%2C0%2C0-2-7.52%2C6.55%2C6.55%2C0%2C0%2C0-6-3.22%2C24.73%2C24.73%2C0%2C0%2C0-9.25%2C1.54Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M112.36%2C255.94q0-7.71%2C4.09-12.3a13.75%2C13.75%2C0%2C0%2C1%2C10.8-4.59q13.35%2C0%2C13.36%2C18.86H117.79a12.3%2C12.3%2C0%2C0%2C0%2C2.9%2C7.07q2.59%2C3.11%2C7.9%2C3.1a24.92%2C24.92%2C0%2C0%2C0%2C10.55-2v5a27.74%2C27.74%2C0%2C0%2C1-9.86%2C1.87%2C19.83%2C19.83%2C0%2C0%2C1-7.7-1.37%2C13.31%2C13.31%2C0%2C0%2C1-5.28-3.76%2C16.21%2C16.21%2C0%2C0%2C1-3-5.38A20.84%2C20.84%2C0%2C0%2C1%2C112.36%2C255.94Zm5.62-2.12h17.26a14.91%2C14.91%2C0%2C0%2C0-2.37-7.12%2C6.44%2C6.44%2C0%2C0%2C0-5.62-2.78%2C8.2%2C8.2%2C0%2C0%2C0-6.21%2C2.72A12.07%2C12.07%2C0%2C0%2C0%2C118%2C253.82Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M147.32%2C244.89V240h5v-7.59a8.14%2C8.14%2C0%2C0%2C1%2C2.31-6.05%2C7.79%2C7.79%2C0%2C0%2C1%2C5.69-2.28h7.86V229h-5c-2.21%2C0-3.67.45-4.37%2C1.34s-1.06%2C2.55-1.06%2C5V240h8.46v4.87h-8.46V272h-5.44v-27.1Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M175.26%2C272V224h5.43v48Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M194.41%2C268.05a17.86%2C17.86%2C0%2C1%2C1%2C12.33%2C4.9A16.57%2C16.57%2C0%2C0%2C1%2C194.41%2C268.05Zm3.84-20.65a13.16%2C13.16%2C0%2C0%2C0%2C0%2C17.2%2C12.07%2C12.07%2C0%2C0%2C0%2C17%2C0%2C13.09%2C13.09%2C0%2C0%2C0%2C0-17.2%2C12.07%2C12.07%2C0%2C0%2C0-17%2C0Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M228.45%2C240h5.75l7.3%2C25.32L248.93%2C240h5.36l7.34%2C25.34L269%2C240h5.74L264.7%2C272h-6.12l-6.83-24.58L245%2C272h-6.47Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E",
diff --git a/elyra/tests/pipeline/resources/validation_pipelines/kf_supernode_valid.pipeline b/elyra/tests/pipeline/resources/validation_pipelines/kf_supernode_valid.pipeline
index 3429ecb4b..bdf4d2c63 100644
--- a/elyra/tests/pipeline/resources/validation_pipelines/kf_supernode_valid.pipeline
+++ b/elyra/tests/pipeline/resources/validation_pipelines/kf_supernode_valid.pipeline
@@ -19,7 +19,7 @@
"output_data": ""
},
"label": "",
- "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/1.6.0/components/web/Download/component.yaml",
+ "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/web/Download/component-sdk-v2.yaml",
"ui_data": {
"label": "Download data",
"image": "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20276.93%20274.55%22%3E%3Cg%20id%3D%22Layer_2%22%20data-name%3D%22Layer%202%22%3E%3Cg%20id%3D%22Layer_1-2%22%20data-name%3D%22Layer%201%22%3E%3Cpath%20d%3D%22M95.9%2C62.15%2C100%2C164.25l73.75-94.12a6.79%2C6.79%2C0%2C0%2C1%2C9.6-1.11l46%2C36.92-15-65.61Z%22%20fill%3D%22%234279f4%22%2F%3E%3Cpolygon%20points%3D%22102.55%20182.98%20167.97%20182.98%20127.8%20150.75%20102.55%20182.98%22%20fill%3D%22%230028aa%22%2F%3E%3Cpolygon%20points%3D%22180.18%2083.92%20136.18%20140.06%20183.06%20177.67%20227.53%20121.91%20180.18%2083.92%22%20fill%3D%22%23014bd1%22%2F%3E%3Cpolygon%20points%3D%2283.56%2052.3%2083.57%2052.29%20122.26%203.77%2059.87%2033.82%2044.46%20101.33%2083.56%2052.3%22%20fill%3D%22%23bedcff%22%2F%3E%3Cpolygon%20points%3D%2245.32%20122.05%2086.76%20174.01%2082.81%2075.03%2045.32%20122.05%22%20fill%3D%22%236ca1ff%22%2F%3E%3Cpolygon%20points%3D%22202.31%2028.73%20142.65%200%20105.52%2046.56%20202.31%2028.73%22%20fill%3D%22%23a1c3ff%22%2F%3E%3Cpath%20d%3D%22M1.6%2C272V227.22H7.34v23.41l20.48-23.41h6.4l-17.39%2C19.7%2C19%2C25.07H29.1l-15.92-20.8-5.84%2C6.65V272Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M41.62%2C262.21V240h5.43v22.39a4.67%2C4.67%2C0%2C0%2C0%2C2.35%2C4.19%2C11%2C11%2C0%2C0%2C0%2C11%2C0%2C4.69%2C4.69%2C0%2C0%2C0%2C2.33-4.19V240h5.43v22.19a9.08%2C9.08%2C0%2C0%2C1-4.1%2C7.87%2C16.2%2C16.2%2C0%2C0%2C1-18.37%2C0A9.07%2C9.07%2C0%2C0%2C1%2C41.62%2C262.21Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M77.46%2C272V224h5.43v16.81a29.29%2C29.29%2C0%2C0%2C1%2C9.32-1.73%2C13.1%2C13.1%2C0%2C0%2C1%2C6.2%2C1.41%2C10.71%2C10.71%2C0%2C0%2C1%2C4.18%2C3.74%2C18.07%2C18.07%2C0%2C0%2C1%2C2.23%2C5.06%2C21.26%2C21.26%2C0%2C0%2C1%2C.73%2C5.58q0%2C8.43-4.38%2C12.79T87.35%2C272Zm5.43-4.87h4.55q6.77%2C0%2C9.72-2.95t3-9.51a14.21%2C14.21%2C0%2C0%2C0-2-7.52%2C6.55%2C6.55%2C0%2C0%2C0-6-3.22%2C24.73%2C24.73%2C0%2C0%2C0-9.25%2C1.54Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M112.36%2C255.94q0-7.71%2C4.09-12.3a13.75%2C13.75%2C0%2C0%2C1%2C10.8-4.59q13.35%2C0%2C13.36%2C18.86H117.79a12.3%2C12.3%2C0%2C0%2C0%2C2.9%2C7.07q2.59%2C3.11%2C7.9%2C3.1a24.92%2C24.92%2C0%2C0%2C0%2C10.55-2v5a27.74%2C27.74%2C0%2C0%2C1-9.86%2C1.87%2C19.83%2C19.83%2C0%2C0%2C1-7.7-1.37%2C13.31%2C13.31%2C0%2C0%2C1-5.28-3.76%2C16.21%2C16.21%2C0%2C0%2C1-3-5.38A20.84%2C20.84%2C0%2C0%2C1%2C112.36%2C255.94Zm5.62-2.12h17.26a14.91%2C14.91%2C0%2C0%2C0-2.37-7.12%2C6.44%2C6.44%2C0%2C0%2C0-5.62-2.78%2C8.2%2C8.2%2C0%2C0%2C0-6.21%2C2.72A12.07%2C12.07%2C0%2C0%2C0%2C118%2C253.82Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M147.32%2C244.89V240h5v-7.59a8.14%2C8.14%2C0%2C0%2C1%2C2.31-6.05%2C7.79%2C7.79%2C0%2C0%2C1%2C5.69-2.28h7.86V229h-5c-2.21%2C0-3.67.45-4.37%2C1.34s-1.06%2C2.55-1.06%2C5V240h8.46v4.87h-8.46V272h-5.44v-27.1Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M175.26%2C272V224h5.43v48Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M194.41%2C268.05a17.86%2C17.86%2C0%2C1%2C1%2C12.33%2C4.9A16.57%2C16.57%2C0%2C0%2C1%2C194.41%2C268.05Zm3.84-20.65a13.16%2C13.16%2C0%2C0%2C0%2C0%2C17.2%2C12.07%2C12.07%2C0%2C0%2C0%2C17%2C0%2C13.09%2C13.09%2C0%2C0%2C0%2C0-17.2%2C12.07%2C12.07%2C0%2C0%2C0-17%2C0Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M228.45%2C240h5.75l7.3%2C25.32L248.93%2C240h5.36l7.34%2C25.34L269%2C240h5.74L264.7%2C272h-6.12l-6.83-24.58L245%2C272h-6.47Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E",
@@ -263,7 +263,7 @@
}
},
"label": "",
- "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/1.6.0/components/basics/Calculate_hash/component.yaml",
+ "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/basics/Calculate_hash/component.yaml",
"ui_data": {
"label": "Calculate data hash",
"image": "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20276.93%20274.55%22%3E%3Cg%20id%3D%22Layer_2%22%20data-name%3D%22Layer%202%22%3E%3Cg%20id%3D%22Layer_1-2%22%20data-name%3D%22Layer%201%22%3E%3Cpath%20d%3D%22M95.9%2C62.15%2C100%2C164.25l73.75-94.12a6.79%2C6.79%2C0%2C0%2C1%2C9.6-1.11l46%2C36.92-15-65.61Z%22%20fill%3D%22%234279f4%22%2F%3E%3Cpolygon%20points%3D%22102.55%20182.98%20167.97%20182.98%20127.8%20150.75%20102.55%20182.98%22%20fill%3D%22%230028aa%22%2F%3E%3Cpolygon%20points%3D%22180.18%2083.92%20136.18%20140.06%20183.06%20177.67%20227.53%20121.91%20180.18%2083.92%22%20fill%3D%22%23014bd1%22%2F%3E%3Cpolygon%20points%3D%2283.56%2052.3%2083.57%2052.29%20122.26%203.77%2059.87%2033.82%2044.46%20101.33%2083.56%2052.3%22%20fill%3D%22%23bedcff%22%2F%3E%3Cpolygon%20points%3D%2245.32%20122.05%2086.76%20174.01%2082.81%2075.03%2045.32%20122.05%22%20fill%3D%22%236ca1ff%22%2F%3E%3Cpolygon%20points%3D%22202.31%2028.73%20142.65%200%20105.52%2046.56%20202.31%2028.73%22%20fill%3D%22%23a1c3ff%22%2F%3E%3Cpath%20d%3D%22M1.6%2C272V227.22H7.34v23.41l20.48-23.41h6.4l-17.39%2C19.7%2C19%2C25.07H29.1l-15.92-20.8-5.84%2C6.65V272Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M41.62%2C262.21V240h5.43v22.39a4.67%2C4.67%2C0%2C0%2C0%2C2.35%2C4.19%2C11%2C11%2C0%2C0%2C0%2C11%2C0%2C4.69%2C4.69%2C0%2C0%2C0%2C2.33-4.19V240h5.43v22.19a9.08%2C9.08%2C0%2C0%2C1-4.1%2C7.87%2C16.2%2C16.2%2C0%2C0%2C1-18.37%2C0A9.07%2C9.07%2C0%2C0%2C1%2C41.62%2C262.21Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M77.46%2C272V224h5.43v16.81a29.29%2C29.29%2C0%2C0%2C1%2C9.32-1.73%2C13.1%2C13.1%2C0%2C0%2C1%2C6.2%2C1.41%2C10.71%2C10.71%2C0%2C0%2C1%2C4.18%2C3.74%2C18.07%2C18.07%2C0%2C0%2C1%2C2.23%2C5.06%2C21.26%2C21.26%2C0%2C0%2C1%2C.73%2C5.58q0%2C8.43-4.38%2C12.79T87.35%2C272Zm5.43-4.87h4.55q6.77%2C0%2C9.72-2.95t3-9.51a14.21%2C14.21%2C0%2C0%2C0-2-7.52%2C6.55%2C6.55%2C0%2C0%2C0-6-3.22%2C24.73%2C24.73%2C0%2C0%2C0-9.25%2C1.54Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M112.36%2C255.94q0-7.71%2C4.09-12.3a13.75%2C13.75%2C0%2C0%2C1%2C10.8-4.59q13.35%2C0%2C13.36%2C18.86H117.79a12.3%2C12.3%2C0%2C0%2C0%2C2.9%2C7.07q2.59%2C3.11%2C7.9%2C3.1a24.92%2C24.92%2C0%2C0%2C0%2C10.55-2v5a27.74%2C27.74%2C0%2C0%2C1-9.86%2C1.87%2C19.83%2C19.83%2C0%2C0%2C1-7.7-1.37%2C13.31%2C13.31%2C0%2C0%2C1-5.28-3.76%2C16.21%2C16.21%2C0%2C0%2C1-3-5.38A20.84%2C20.84%2C0%2C0%2C1%2C112.36%2C255.94Zm5.62-2.12h17.26a14.91%2C14.91%2C0%2C0%2C0-2.37-7.12%2C6.44%2C6.44%2C0%2C0%2C0-5.62-2.78%2C8.2%2C8.2%2C0%2C0%2C0-6.21%2C2.72A12.07%2C12.07%2C0%2C0%2C0%2C118%2C253.82Z%22%20fill%3D%22%234279f4%22%20stroke%3D%22%234279f4%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M147.32%2C244.89V240h5v-7.59a8.14%2C8.14%2C0%2C0%2C1%2C2.31-6.05%2C7.79%2C7.79%2C0%2C0%2C1%2C5.69-2.28h7.86V229h-5c-2.21%2C0-3.67.45-4.37%2C1.34s-1.06%2C2.55-1.06%2C5V240h8.46v4.87h-8.46V272h-5.44v-27.1Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M175.26%2C272V224h5.43v48Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M194.41%2C268.05a17.86%2C17.86%2C0%2C1%2C1%2C12.33%2C4.9A16.57%2C16.57%2C0%2C0%2C1%2C194.41%2C268.05Zm3.84-20.65a13.16%2C13.16%2C0%2C0%2C0%2C0%2C17.2%2C12.07%2C12.07%2C0%2C0%2C0%2C17%2C0%2C13.09%2C13.09%2C0%2C0%2C0%2C0-17.2%2C12.07%2C12.07%2C0%2C0%2C0-17%2C0Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3Cpath%20d%3D%22M228.45%2C240h5.75l7.3%2C25.32L248.93%2C240h5.36l7.34%2C25.34L269%2C240h5.74L264.7%2C272h-6.12l-6.83-24.58L245%2C272h-6.47Z%22%20fill%3D%22%230028aa%22%20stroke%3D%22%230028aa%22%20stroke-miterlimit%3D%2210%22%20stroke-width%3D%223.2%22%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E",
diff --git a/elyra/tests/pipeline/resources/validation_pipelines/kf_with_parameters.pipeline b/elyra/tests/pipeline/resources/validation_pipelines/kf_with_parameters.pipeline
index 9e7b0d52b..65dbe1278 100644
--- a/elyra/tests/pipeline/resources/validation_pipelines/kf_with_parameters.pipeline
+++ b/elyra/tests/pipeline/resources/validation_pipelines/kf_with_parameters.pipeline
@@ -47,7 +47,7 @@
"output_data": ""
},
"label": "",
- "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/1.6.0/components/web/Download/component.yaml",
+ "component_source": "https://raw.githubusercontent.com/kubeflow/pipelines/sdk-2.8.0/components/contrib/web/Download/component-sdk-v2.yaml",
"ui_data": {
"label": "Download data",
"image": "/static/elyra/kubeflow.svg",
diff --git a/etc/docker/elyra_development/requirements.yml b/etc/docker/elyra_development/requirements.yml
index d6e4aa47a..0fc3ccf58 100644
--- a/etc/docker/elyra_development/requirements.yml
+++ b/etc/docker/elyra_development/requirements.yml
@@ -1,5 +1,5 @@
name: elyra-env
dependencies:
- - python>=3.8
+ - python>=3.11
- pip:
- - jupyterlab>=3.4.6
+ - jupyterlab==4.2.5
diff --git a/etc/generic/requirements-elyra.txt b/etc/generic/requirements-elyra.txt
index 313c6a781..07ab52e72 100644
--- a/etc/generic/requirements-elyra.txt
+++ b/etc/generic/requirements-elyra.txt
@@ -1,22 +1,22 @@
# This is a comprehensive list of python dependencies that Elyra requires to execute Jupyter notebooks.
-ipykernel==6.13.0
-ipython==8.10.0
+ipykernel==6.25.2
+ipython==8.12.3
ipython-genutils==0.2.0
-jinja2==3.0.3
-jupyter-client==7.3.1
-jupyter-core==4.11.2
+jinja2==3.1.4
+jupyter-client==7.4.9
+jupyter-core==5.3.1
MarkupSafe==2.1.1
-minio==7.1.8
+minio==7.1.15
nbclient==0.6.3
-nbconvert==6.5.1
+nbconvert==7.1.0
nbformat==5.4.0
-papermill==2.3.4
+papermill==2.6.0
pyzmq==24.0.1
-prompt-toolkit==3.0.30
-requests==2.31.0
-tornado==6.3.3
-traitlets==5.1.1
-urllib3==1.26.18
+prompt-toolkit==3.0.43
+requests==2.32.3
+tornado==6.4.1
+traitlets==5.10.0
+urllib3==1.26.19
#
# These excluded are transitive dependencies of the included python packages.
#ansiwrap==0.8.4
diff --git a/etc/scripts/generate-make-graph.ts b/etc/scripts/generate-make-graph.ts
index 9068990d1..adc1912e4 100644
--- a/etc/scripts/generate-make-graph.ts
+++ b/etc/scripts/generate-make-graph.ts
@@ -63,7 +63,7 @@ make.stdout.on('data', (data: Buffer) => {
graph.push({
type: 'target',
name,
- depth: depthString.length / 2,
+ depth: depthString.length / 2
});
continue;
}
@@ -79,7 +79,7 @@ make.stdout.on('data', (data: Buffer) => {
depth = depthString.length / 2;
graph.push({
type: 'end',
- depth,
+ depth
});
depth = undefined;
continue;
@@ -89,7 +89,7 @@ make.stdout.on('data', (data: Buffer) => {
graph.push({
type: 'code',
value: msg.toString(),
- depth,
+ depth
});
continue;
}
@@ -107,7 +107,7 @@ const printGraph = (): void => {
const spacer = ' '.repeat(cellWidth - g.name.length - 2);
console.log(`${padLeft}┌${bar}┐${padRight}`);
console.log(
- `${padLeft}│ ${chalk.cyan.bold(g.name)}${spacer} |${padRight}`,
+ `${padLeft}│ ${chalk.cyan.bold(g.name)}${spacer} |${padRight}`
);
continue;
}
diff --git a/labextensions/elyra_code_snippet_extension/__init__.py b/labextensions/elyra_code_snippet_extension/__init__.py
new file mode 100644
index 000000000..e50fe87fd
--- /dev/null
+++ b/labextensions/elyra_code_snippet_extension/__init__.py
@@ -0,0 +1,14 @@
+try:
+ from ._version import __version__
+except ImportError:
+ # Fallback when using the package in dev mode without installing
+ # in editable mode with pip. It is highly recommended to install
+ # the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
+ import warnings
+
+ warnings.warn("Importing 'elyra_code_snippet_extension' outside a proper installation.")
+ __version__ = "dev"
+
+
+def _jupyter_labextension_paths():
+ return [{"src": "labextension", "dest": "@elyra/code-snippet-extension"}]
diff --git a/labextensions/elyra_metadata_common/__init__.py b/labextensions/elyra_metadata_common/__init__.py
new file mode 100644
index 000000000..ada029c9c
--- /dev/null
+++ b/labextensions/elyra_metadata_common/__init__.py
@@ -0,0 +1,14 @@
+try:
+ from ._version import __version__
+except ImportError:
+ # Fallback when using the package in dev mode without installing
+ # in editable mode with pip. It is highly recommended to install
+ # the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
+ import warnings
+
+ warnings.warn("Importing 'elyra_metadata_comomn' outside a proper installation.")
+ __version__ = "dev"
+
+
+def _jupyter_labextension_paths():
+ return [{"src": "labextension", "dest": "@elyra/metadata-common"}]
diff --git a/labextensions/elyra_metadata_extension/__init__.py b/labextensions/elyra_metadata_extension/__init__.py
new file mode 100644
index 000000000..c9ad80568
--- /dev/null
+++ b/labextensions/elyra_metadata_extension/__init__.py
@@ -0,0 +1,14 @@
+try:
+ from ._version import __version__
+except ImportError:
+ # Fallback when using the package in dev mode without installing
+ # in editable mode with pip. It is highly recommended to install
+ # the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
+ import warnings
+
+ warnings.warn("Importing 'elyra_metadata_extension' outside a proper installation.")
+ __version__ = "dev"
+
+
+def _jupyter_labextension_paths():
+ return [{"src": "labextension", "dest": "@elyra/metadata-extension"}]
diff --git a/labextensions/elyra_pipeline_editor_extension/__init__.py b/labextensions/elyra_pipeline_editor_extension/__init__.py
new file mode 100644
index 000000000..9321d169b
--- /dev/null
+++ b/labextensions/elyra_pipeline_editor_extension/__init__.py
@@ -0,0 +1,14 @@
+try:
+ from ._version import __version__
+except ImportError:
+ # Fallback when using the package in dev mode without installing
+ # in editable mode with pip. It is highly recommended to install
+ # the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
+ import warnings
+
+ warnings.warn("Importing 'elyra_pipeline_editor_extension' outside a proper installation.")
+ __version__ = "dev"
+
+
+def _jupyter_labextension_paths():
+ return [{"src": "labextension", "dest": "@elyra/pipeline-editor-extension"}]
diff --git a/labextensions/elyra_python_editor_extension/__init__.py b/labextensions/elyra_python_editor_extension/__init__.py
new file mode 100644
index 000000000..6aaf2988f
--- /dev/null
+++ b/labextensions/elyra_python_editor_extension/__init__.py
@@ -0,0 +1,14 @@
+try:
+ from ._version import __version__
+except ImportError:
+ # Fallback when using the package in dev mode without installing
+ # in editable mode with pip. It is highly recommended to install
+ # the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
+ import warnings
+
+ warnings.warn("Importing 'elyra_python_editor_extension' outside a proper installation.")
+ __version__ = "dev"
+
+
+def _jupyter_labextension_paths():
+ return [{"src": "labextension", "dest": "@elyra/python-editor-extension"}]
diff --git a/labextensions/elyra_script_debugger_extension/__init__.py b/labextensions/elyra_script_debugger_extension/__init__.py
new file mode 100644
index 000000000..2e078f4f5
--- /dev/null
+++ b/labextensions/elyra_script_debugger_extension/__init__.py
@@ -0,0 +1,14 @@
+try:
+ from ._version import __version__
+except ImportError:
+ # Fallback when using the package in dev mode without installing
+ # in editable mode with pip. It is highly recommended to install
+ # the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
+ import warnings
+
+ warnings.warn("Importing 'elyra_script_debugger_extension' outside a proper installation.")
+ __version__ = "dev"
+
+
+def _jupyter_labextension_paths():
+ return [{"src": "labextension", "dest": "@elyra/script-debugger-extension"}]
diff --git a/labextensions/elyra_script_editor/__init__.py b/labextensions/elyra_script_editor/__init__.py
new file mode 100644
index 000000000..da999da45
--- /dev/null
+++ b/labextensions/elyra_script_editor/__init__.py
@@ -0,0 +1,14 @@
+try:
+ from ._version import __version__
+except ImportError:
+ # Fallback when using the package in dev mode without installing
+ # in editable mode with pip. It is highly recommended to install
+ # the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
+ import warnings
+
+ warnings.warn("Importing 'elyra_script_editor' outside a proper installation.")
+ __version__ = "dev"
+
+
+def _jupyter_labextension_paths():
+ return [{"src": "labextension", "dest": "@elyra/script-editor"}]
diff --git a/labextensions/elyra_services/__init__.py b/labextensions/elyra_services/__init__.py
new file mode 100644
index 000000000..928404622
--- /dev/null
+++ b/labextensions/elyra_services/__init__.py
@@ -0,0 +1,14 @@
+try:
+ from ._version import __version__
+except ImportError:
+ # Fallback when using the package in dev mode without installing
+ # in editable mode with pip. It is highly recommended to install
+ # the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
+ import warnings
+
+ warnings.warn("Importing 'elyra_services' outside a proper installation.")
+ __version__ = "dev"
+
+
+def _jupyter_labextension_paths():
+ return [{"src": "labextension", "dest": "@elyra/services"}]
diff --git a/labextensions/elyra_theme_extension/__init__.py b/labextensions/elyra_theme_extension/__init__.py
new file mode 100644
index 000000000..bc4647318
--- /dev/null
+++ b/labextensions/elyra_theme_extension/__init__.py
@@ -0,0 +1,14 @@
+try:
+ from ._version import __version__
+except ImportError:
+ # Fallback when using the package in dev mode without installing
+ # in editable mode with pip. It is highly recommended to install
+ # the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
+ import warnings
+
+ warnings.warn("Importing 'elyra_theme_extension' outside a proper installation.")
+ __version__ = "dev"
+
+
+def _jupyter_labextension_paths():
+ return [{"src": "labextension", "dest": "@elyra/theme-extension"}]
diff --git a/labextensions/elyra_ui_components/__init__.py b/labextensions/elyra_ui_components/__init__.py
new file mode 100644
index 000000000..294cd1806
--- /dev/null
+++ b/labextensions/elyra_ui_components/__init__.py
@@ -0,0 +1,14 @@
+try:
+ from ._version import __version__
+except ImportError:
+ # Fallback when using the package in dev mode without installing
+ # in editable mode with pip. It is highly recommended to install
+ # the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
+ import warnings
+
+ warnings.warn("Importing 'elyra_ui_components' outside a proper installation.")
+ __version__ = "dev"
+
+
+def _jupyter_labextension_paths():
+ return [{"src": "labextension", "dest": "@elyra/ui-components"}]
diff --git a/package.json b/package.json
index ddeb5d53f..a26501779 100644
--- a/package.json
+++ b/package.json
@@ -45,11 +45,11 @@
"yjs": "^13.5.40"
},
"devDependencies": {
- "@4tw/cypress-drag-drop": "^1.3.1",
"@cypress/webpack-preprocessor": "^5.5.0",
- "@jupyterlab/testutils": "^4.0.6",
+ "@glen/jest-raw-loader": "^2.0.0",
+ "@jupyterlab/testutils": "^4.2.5",
"@testing-library/cypress": "^7.0.4",
- "@types/jest": "^26.0.20",
+ "@types/jest": "^29.2.0",
"@types/lodash": "^4.14.170",
"@types/node": "^15.0.1",
"@types/react": "^18.0.26",
@@ -57,6 +57,7 @@
"@typescript-eslint/eslint-plugin": "~6.13.2",
"@typescript-eslint/parser": "~6.13.2",
"cypress": "^6.2.0",
+ "cypress-real-events": "^1.13.0",
"eslint": "~8.55.0",
"eslint-config-prettier": "~9.1.0",
"eslint-plugin-cypress": "^2.15.1",
@@ -68,21 +69,19 @@
"eslint-plugin-react": "~7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"husky": "^8.0.3",
- "jest": "^29.7.0",
- "jest-raw-loader": "^1.0.1",
+ "jest": "^29.2.0",
"lerna": "^8.0.1",
"lint-staged": "^15.2.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.1.1",
"rimraf": "~5.0.5",
"start-server-and-test": "^2.0.3",
- "ts-jest": "^29.1.1",
+ "ts-jest": "^29.2.5",
"ts-loader": "^9.5.1",
"ts-node": "^10.9.2",
"typedoc": "~0.24.7",
"typedoc-plugin-mdn-links": "^3.0.3",
"typescript": "~5.1.6",
"webpack": "^5.76.1"
- },
- "packageManager": "yarn@3.5.0"
+ }
}
diff --git a/packages/code-snippet/install.json b/packages/code-snippet/install.json
new file mode 100644
index 000000000..622ced017
--- /dev/null
+++ b/packages/code-snippet/install.json
@@ -0,0 +1,5 @@
+{
+ "packageManager": "python",
+ "packageName": "elyra_code_snippet_extension",
+ "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package elyra_code_snippet_extension"
+}
diff --git a/packages/code-snippet/package.json b/packages/code-snippet/package.json
index ec8c59748..eebcf1bc5 100644
--- a/packages/code-snippet/package.json
+++ b/packages/code-snippet/package.json
@@ -25,40 +25,50 @@
"style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}"
],
"scripts": {
- "build": "jlpm run build:lib && jlpm run build:labextension:dev",
- "build:prod": "jlpm run build:lib && jlpm run build:labextension",
- "build:lib": "tsc",
+ "build": "jlpm build:lib && jlpm build:labextension:dev",
+ "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension",
"build:labextension": "jupyter labextension build .",
"build:labextension:dev": "jupyter labextension build --development True .",
- "clean": "rimraf lib tsconfig.tsbuildinfo ../../build/labextensions/@elyra/code-snippet-extension",
- "lab:dev": "jupyter labextension develop --overwrite ../../build/labextensions/@elyra/code-snippet-extension",
- "dist": "npm pack .",
- "prepare": "npm run build",
+ "build:lib": "tsc --sourceMap",
+ "build:lib:prod": "tsc",
+ "clean": "jlpm clean:lib",
+ "clean:lib": "rimraf lib tsconfig.tsbuildinfo",
+ "clean:lintcache": "rimraf .eslintcache .stylelintcache",
+ "clean:labextension": "rimraf ../../../../labextensions/elyra_code_snippet_extension/labextension ../../../../labextensions/elyra_code_snippet_extension/_version.py",
+ "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache",
+ "eslint": "jlpm eslint:check --fix",
+ "eslint:check": "eslint . --cache --ext .ts,.tsx",
+ "install:extension": "jlpm build",
+ "lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
+ "lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
+ "prettier": "jlpm prettier:base --write --list-different",
+ "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
+ "prettier:check": "jlpm prettier:base --check",
+ "stylelint": "jlpm stylelint:check --fix",
+ "stylelint:check": "stylelint --cache \"style/**/*.css\"",
+ "test": "jest --coverage --passWithNoTests",
"watch": "run-p watch:src watch:labextension",
- "watch:src": "tsc -w",
- "watch:labextension": "jupyter labextension watch .",
- "lab:install": "jupyter labextension install --no-build",
- "lab:uninstall": "jupyter labextension uninstall --no-build",
- "link:dev": "yarn link @jupyterlab/builder",
- "unlink:dev": "yarn unlink @jupyterlab/builder"
+ "watch:src": "tsc -w --sourceMap",
+ "watch:labextension": "jupyter labextension watch ."
},
"dependencies": {
"@elyra/metadata-common": "4.0.0-dev",
"@elyra/services": "4.0.0-dev",
"@elyra/ui-components": "4.0.0-dev",
- "@jupyterlab/application": "^4.0.6",
- "@jupyterlab/apputils": "^4.1.6",
- "@jupyterlab/builder": "^4.0.6",
- "@jupyterlab/cells": "^4.0.6",
- "@jupyterlab/codeeditor": "^4.0.6",
+ "@jupyterlab/application": "^4.2.5",
+ "@jupyterlab/apputils": "^4.2.5",
+ "@jupyterlab/builder": "^4.2.5",
+ "@jupyterlab/cells": "^4.2.5",
+ "@jupyterlab/codeeditor": "^4.2.5",
+ "@jupyterlab/codemirror": "^4.2.5",
"@jupyterlab/coreutils": "^6.0.6",
- "@jupyterlab/docmanager": "^4.0.6",
- "@jupyterlab/docregistry": "^4.0.6",
- "@jupyterlab/fileeditor": "^4.0.6",
- "@jupyterlab/mainmenu": "^4.0.6",
- "@jupyterlab/markdownviewer": "^4.0.6",
- "@jupyterlab/notebook": "^4.0.6",
- "@jupyterlab/ui-components": "^4.0.6",
+ "@jupyterlab/docmanager": "^4.2.5",
+ "@jupyterlab/docregistry": "^4.2.5",
+ "@jupyterlab/fileeditor": "^4.2.5",
+ "@jupyterlab/mainmenu": "^4.2.5",
+ "@jupyterlab/markdownviewer": "^4.2.5",
+ "@jupyterlab/notebook": "^4.2.5",
+ "@jupyterlab/ui-components": "^4.2.5",
"@lumino/algorithm": "*",
"@lumino/coreutils": "^2.1.2",
"@lumino/messaging": "^2.0.1",
@@ -78,6 +88,6 @@
},
"jupyterlab": {
"extension": true,
- "outputDir": "../../build/labextensions/@elyra/code-snippet-extension"
+ "outputDir": "../../labextensions/elyra_code_snippet_extension/labextension"
}
}
diff --git a/packages/code-snippet/setup.py b/packages/code-snippet/setup.py
new file mode 100644
index 000000000..aefdf20db
--- /dev/null
+++ b/packages/code-snippet/setup.py
@@ -0,0 +1 @@
+__import__("setuptools").setup()
diff --git a/packages/code-snippet/src/CodeSnippetService.ts b/packages/code-snippet/src/CodeSnippetService.ts
index 3ff85c777..94693b46f 100644
--- a/packages/code-snippet/src/CodeSnippetService.ts
+++ b/packages/code-snippet/src/CodeSnippetService.ts
@@ -57,13 +57,13 @@ export class CodeSnippetService {
static deleteCodeSnippet(codeSnippet: IMetadata): Promise {
return showDialog({
title: `Delete snippet '${codeSnippet.display_name}'?`,
- buttons: [Dialog.cancelButton(), Dialog.okButton()],
+ buttons: [Dialog.cancelButton(), Dialog.okButton()]
}).then((result: any) => {
// Do nothing if the cancel button is pressed
if (result.button.accept) {
return MetadataService.deleteMetadata(
CODE_SNIPPET_SCHEMASPACE,
- codeSnippet.name,
+ codeSnippet.name
).then(() => true);
} else {
return false;
diff --git a/packages/code-snippet/src/CodeSnippetWidget.tsx b/packages/code-snippet/src/CodeSnippetWidget.tsx
index f28f06f56..aa07478f9 100644
--- a/packages/code-snippet/src/CodeSnippetWidget.tsx
+++ b/packages/code-snippet/src/CodeSnippetWidget.tsx
@@ -25,13 +25,13 @@ import {
MetadataCommonService,
MetadataDisplay,
MetadataWidget,
- METADATA_ITEM,
+ METADATA_ITEM
} from '@elyra/metadata-common';
import {
ExpandableComponent,
importIcon,
RequestErrors,
- trashIcon,
+ trashIcon
} from '@elyra/ui-components';
import { JupyterFrontEnd } from '@jupyterlab/application';
@@ -40,22 +40,23 @@ import {
CodeCell,
MarkdownCell,
ICodeCellModel,
- IMarkdownCellModel /*RawCell*/,
+ IMarkdownCellModel /*RawCell*/
} from '@jupyterlab/cells';
import { CodeEditor, IEditorServices } from '@jupyterlab/codeeditor';
+import { EditorLanguageRegistry } from '@jupyterlab/codemirror';
import { PathExt } from '@jupyterlab/coreutils';
import { DocumentWidget } from '@jupyterlab/docregistry';
import { FileEditor } from '@jupyterlab/fileeditor';
import * as nbformat from '@jupyterlab/nbformat';
import {
Notebook,
- /*NotebookModel,*/ NotebookPanel /*,NotebookActions*/,
+ /*NotebookModel,*/ NotebookPanel /*,NotebookActions*/
} from '@jupyterlab/notebook';
import {
copyIcon,
editIcon,
pasteIcon,
- LabIcon,
+ LabIcon
} from '@jupyterlab/ui-components';
import { find } from '@lumino/algorithm';
@@ -70,7 +71,7 @@ import React from 'react';
import {
CodeSnippetService,
CODE_SNIPPET_SCHEMASPACE,
- CODE_SNIPPET_SCHEMA,
+ CODE_SNIPPET_SCHEMA
} from './CodeSnippetService';
const METADATA_EDITOR_ID = 'elyra-metadata-editor';
@@ -138,7 +139,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
snippetLanguage.toLowerCase() !== 'markdown'
) {
fileEditor.replaceSelection?.(
- this.addMarkdownCodeBlock(snippetLanguage, codeSnippet),
+ this.addMarkdownCodeBlock(snippetLanguage, codeSnippet)
);
} else if (editorLanguage) {
this.verifyLanguageAndInsert(snippet, editorLanguage, fileEditor);
@@ -165,14 +166,14 @@ class CodeSnippetDisplay extends MetadataDisplay {
this.verifyLanguageAndInsert(
snippet,
kernelLanguage,
- notebookCellEditor,
+ notebookCellEditor
);
} else if (
notebookCell instanceof MarkdownCell &&
snippetLanguage.toLowerCase() !== 'markdown'
) {
notebookCellEditor.replaceSelection?.(
- this.addMarkdownCodeBlock(snippetLanguage, codeSnippet),
+ this.addMarkdownCodeBlock(snippetLanguage, codeSnippet)
);
} else {
notebookCellEditor.replaceSelection?.(codeSnippet);
@@ -183,14 +184,14 @@ class CodeSnippetDisplay extends MetadataDisplay {
const contentFactory = new NotebookPanel.ContentFactory({
editorFactory:
- this.props.editorServices.factoryService.newInlineEditor,
+ this.props.editorServices.factoryService.newInlineEditor
});
/*
interface CodeCellCreatorOption {
- model: ICodeCellModel | undefined;
- rendermime: RenderMimeRegistry;
- contentFactory: any;
+ model: ICodeCellModel | undefined;
+ rendermime: RenderMimeRegistry;
+ contentFactory: any;
cell_type: string;
}
*/
@@ -198,7 +199,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
const options: CodeCell.IOptions = {
model: notebookContent.activeCell?.model as ICodeCellModel,
rendermime: notebookContent.rendermime,
- contentFactory: contentFactory,
+ contentFactory: contentFactory
};
const codeCell: any = contentFactory.createCodeCell(options);
@@ -209,7 +210,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
// codeCell: SharedCell.Cell
widget.content.model?.sharedModel.insertCell(
activeCellIndex,
- codeCell as Partial & { cell_type: string },
+ codeCell as Partial & { cell_type: string }
);
//update the active cell index to the newly inserted cell
@@ -224,18 +225,19 @@ class CodeSnippetDisplay extends MetadataDisplay {
// Verify if a given widget is a FileEditor
private isFileEditor = (
- widget: Widget,
+ widget: Widget
): widget is DocumentWidget => {
return (widget as DocumentWidget).content instanceof FileEditor;
};
// Return the language of the editor or empty string
private getEditorLanguage = (
- widget: DocumentWidget,
+ widget: DocumentWidget
): string | undefined => {
- const editorLanguage =
- widget.context.sessionContext.kernelPreference.language;
- return editorLanguage === 'null' ? '' : editorLanguage;
+ const editorLanguage = EditorLanguageRegistry.getDefaultLanguages().find(
+ (language) => language.mime.includes(widget.content.model.mimeType)
+ );
+ return editorLanguage?.displayName ?? '';
};
// Return the given code wrapped in a markdown code block
@@ -247,7 +249,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
private verifyLanguageAndInsert = async (
snippet: IMetadata,
editorLanguage: string,
- editor: CodeEditor.IEditor,
+ editor: CodeEditor.IEditor
): Promise => {
const codeSnippet: string = snippet.metadata.code.join('\n');
const snippetLanguage = snippet.metadata.language;
@@ -257,7 +259,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
) {
const result = await this.showWarnDialog(
editorLanguage,
- snippet.display_name,
+ snippet.display_name
);
if (result.button.accept) {
editor.replaceSelection?.(codeSnippet);
@@ -271,12 +273,12 @@ class CodeSnippetDisplay extends MetadataDisplay {
// Display warning dialog when inserting a code snippet incompatible with editor's language
private showWarnDialog = async (
editorLanguage: string,
- snippetName: string,
+ snippetName: string
): Promise> => {
return showDialog({
title: 'Warning',
body: `Code snippet "${snippetName}" is incompatible with ${editorLanguage}. Continue?`,
- buttons: [Dialog.cancelButton(), Dialog.okButton()],
+ buttons: [Dialog.cancelButton(), Dialog.okButton()]
});
};
@@ -285,14 +287,14 @@ class CodeSnippetDisplay extends MetadataDisplay {
return showDialog({
title: 'Error',
body: errMsg,
- buttons: [Dialog.okButton()],
+ buttons: [Dialog.okButton()]
});
};
// Initial setup to handle dragging a code snippet
private handleDragSnippet(
event: React.MouseEvent,
- metadata: IMetadata,
+ metadata: IMetadata
): void {
const { button } = event;
@@ -304,7 +306,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
this._dragData = {
pressX: event.clientX,
pressY: event.clientY,
- dragImage: null,
+ dragImage: null
};
const mouseUpListener = (event: MouseEvent): void => {
@@ -317,7 +319,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
const target = event.target as HTMLElement;
target.addEventListener('mouseup', mouseUpListener, {
once: true,
- capture: true,
+ capture: true
});
target.addEventListener('mousemove', mouseMoveListener, true);
@@ -328,7 +330,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
private _evtMouseUp(
event: MouseEvent,
metadata: IMetadata,
- mouseMoveListener: (event: MouseEvent) => void,
+ mouseMoveListener: (event: MouseEvent) => void
): void {
event.preventDefault();
event.stopPropagation();
@@ -341,7 +343,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
event: MouseEvent,
metadata: IMetadata,
mouseMoveListener: (event: MouseEvent) => void,
- mouseUpListener: (event: MouseEvent) => void,
+ mouseUpListener: (event: MouseEvent) => void
): void {
event.preventDefault();
event.stopPropagation();
@@ -354,7 +356,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
data.pressX,
data.pressY,
event.clientX,
- event.clientY,
+ event.clientY
)
) {
// Create drag image
@@ -372,7 +374,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
data.dragImage,
metadata,
event.clientX,
- event.clientY,
+ event.clientY
);
}
}
@@ -390,7 +392,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
prevX: number,
prevY: number,
nextX: number,
- nextY: number,
+ nextY: number
): boolean {
const dx = Math.abs(nextX - prevX);
const dy = Math.abs(nextY - prevY);
@@ -401,7 +403,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
dragImage: HTMLElement,
metadata: IMetadata,
clientX: number,
- clientY: number,
+ clientY: number
): Promise {
const widget: NotebookPanel =
this.props.getCurrentWidget() as NotebookPanel;
@@ -410,19 +412,19 @@ class CodeSnippetDisplay extends MetadataDisplay {
//const activeCellIndex = notebookContent.activeCellIndex ?? -1;
const contentFactory = new NotebookPanel.ContentFactory({
- editorFactory: this.props.editorServices.factoryService.newInlineEditor,
+ editorFactory: this.props.editorServices.factoryService.newInlineEditor
});
const options: CodeCell.IOptions = {
model: notebookContent.activeCell?.model as ICodeCellModel,
rendermime: notebookContent.rendermime,
- contentFactory: contentFactory,
+ contentFactory: contentFactory
};
const options2: MarkdownCell.IOptions = {
model: notebookContent.activeCell?.model as IMarkdownCellModel,
rendermime: notebookContent.rendermime,
- contentFactory: contentFactory,
+ contentFactory: contentFactory
};
const codeCell = contentFactory.createCodeCell(options);
@@ -455,7 +457,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
dragImage: dragImage,
supportedActions: 'copy-move',
proposedAction: 'copy',
- source: this,
+ source: this
});
const selected: nbformat.ICell[] = [model.model.toJSON()];
@@ -476,14 +478,14 @@ class CodeSnippetDisplay extends MetadataDisplay {
feedback: 'Copied!',
onClick: (): void => {
Clipboard.copyToSystem(metadata.metadata.code.join('\n'));
- },
+ }
},
{
title: 'Insert',
icon: importIcon,
onClick: (): void => {
this.insertCodeSnippet(metadata);
- },
+ }
},
{
title: 'Edit',
@@ -493,9 +495,9 @@ class CodeSnippetDisplay extends MetadataDisplay {
onSave: this.props.updateMetadata,
schemaspace: CODE_SNIPPET_SCHEMASPACE,
schema: CODE_SNIPPET_SCHEMA,
- name: metadata.name,
+ name: metadata.name
});
- },
+ }
},
{
title: 'Duplicate',
@@ -504,13 +506,13 @@ class CodeSnippetDisplay extends MetadataDisplay {
MetadataCommonService.duplicateMetadataInstance(
CODE_SNIPPET_SCHEMASPACE,
metadata,
- this.props.metadata,
+ this.props.metadata
)
.then((response: any): void => {
this.props.updateMetadata();
})
.catch((error) => RequestErrors.serverError(error));
- },
+ }
},
{
title: 'Delete',
@@ -528,7 +530,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
value.id ===
`${METADATA_EDITOR_ID}:${CODE_SNIPPET_SCHEMASPACE}:${CODE_SNIPPET_SCHEMA}:${metadata.name}`
);
- },
+ }
);
if (editorWidget) {
editorWidget.dispose();
@@ -536,8 +538,8 @@ class CodeSnippetDisplay extends MetadataDisplay {
}
})
.catch((error) => RequestErrors.serverError(error));
- },
- },
+ }
+ }
];
};
@@ -547,7 +549,7 @@ class CodeSnippetDisplay extends MetadataDisplay {
sortMetadata(): void {
this.props.metadata.sort((a, b) =>
- this.getDisplayName(a).localeCompare(this.getDisplayName(b)),
+ this.getDisplayName(a).localeCompare(this.getDisplayName(b))
);
}
@@ -593,14 +595,12 @@ class CodeSnippetDisplay extends MetadataDisplay {
createPreviewEditors = (): void => {
const editorFactory =
this.props.editorServices.factoryService.newInlineEditor;
- const getMimeTypeByLanguage =
- this.props.editorServices.mimeTypeService.getMimeTypeByLanguage;
this.props.metadata.map((codeSnippet: IMetadata) => {
+ const content = codeSnippet.metadata.code.join('\n');
+
if (codeSnippet.name in this.editors) {
// Make sure code is up to date
- this.editors[codeSnippet.name].model.selections.has(
- codeSnippet.metadata.code.join('\n'),
- );
+ this.editors[codeSnippet.name].model.sharedModel.setSource(content);
} else {
// Add new snippets
const snippetElement = document.getElementById(codeSnippet.name);
@@ -608,17 +608,20 @@ class CodeSnippetDisplay extends MetadataDisplay {
return;
}
- this.editors[codeSnippet.name] = editorFactory({
+ const mimeType =
+ this.props.editorServices.mimeTypeService.getMimeTypeByLanguage({
+ value: codeSnippet.metadata.code.join('\n'),
+ name: codeSnippet.metadata.language,
+ codemirror_mode: codeSnippet.metadata.language
+ });
+
+ const newEditor = editorFactory({
config: { readOnly: true },
host: snippetElement,
- model: new CodeEditor.Model({
- mimeType: getMimeTypeByLanguage({
- value: codeSnippet.metadata.code.join('\n'),
- name: codeSnippet.metadata.language,
- codemirror_mode: codeSnippet.metadata.language,
- }),
- }),
+ model: new CodeEditor.Model({ mimeType })
});
+ newEditor.model.sharedModel.setSource(content);
+ this.editors[codeSnippet.name] = newEditor;
}
});
};
@@ -663,7 +666,7 @@ export class CodeSnippetWidget extends MetadataWidget {
// Request code snippets from server
async fetchMetadata(): Promise {
return CodeSnippetService.findAll().catch((error) =>
- RequestErrors.serverError(error),
+ RequestErrors.serverError(error)
);
}
diff --git a/packages/code-snippet/src/index.ts b/packages/code-snippet/src/index.ts
index 70abdfef3..41c969bf9 100644
--- a/packages/code-snippet/src/index.ts
+++ b/packages/code-snippet/src/index.ts
@@ -21,11 +21,11 @@ import { codeSnippetIcon } from '@elyra/ui-components';
import {
JupyterFrontEnd,
JupyterFrontEndPlugin,
- ILayoutRestorer,
+ ILayoutRestorer
} from '@jupyterlab/application';
import { ICommandPalette } from '@jupyterlab/apputils';
import { Cell } from '@jupyterlab/cells';
-import { IEditorServices } from '@jupyterlab/codeeditor';
+import { CodeEditor, IEditorServices } from '@jupyterlab/codeeditor';
import { DocumentWidget } from '@jupyterlab/docregistry';
import { FileEditor } from '@jupyterlab/fileeditor';
import { MarkdownDocument } from '@jupyterlab/markdownviewer';
@@ -34,14 +34,14 @@ import { Widget } from '@lumino/widgets';
import {
CODE_SNIPPET_SCHEMASPACE,
- CODE_SNIPPET_SCHEMA,
+ CODE_SNIPPET_SCHEMA
} from './CodeSnippetService';
import { CodeSnippetWidget } from './CodeSnippetWidget';
const CODE_SNIPPET_EXTENSION_ID = 'elyra-code-snippet-extension';
const commandIDs = {
- saveAsSnippet: 'codesnippet:save-as-snippet',
+ saveAsSnippet: 'codesnippet:save-as-snippet'
};
/**
@@ -55,7 +55,7 @@ export const code_snippet_extension: JupyterFrontEndPlugin = {
app: JupyterFrontEnd,
palette: ICommandPalette,
restorer: ILayoutRestorer,
- editorServices: IEditorServices,
+ editorServices: IEditorServices
) => {
console.log('Elyra - code-snippet extension is activated!');
@@ -71,7 +71,8 @@ export const code_snippet_extension: JupyterFrontEndPlugin = {
icon: codeSnippetIcon,
getCurrentWidget,
editorServices,
- titleContext: 'code snippet',
+ titleContext: '',
+ addLabel: 'code snippet'
});
const codeSnippetWidgetId = `elyra-metadata:${CODE_SNIPPET_SCHEMASPACE}`;
codeSnippetWidget.id = codeSnippetWidgetId;
@@ -126,7 +127,7 @@ export const code_snippet_extension: JupyterFrontEndPlugin = {
schemaspace: CODE_SNIPPET_SCHEMASPACE,
schema: CODE_SNIPPET_SCHEMA,
code: selection.split('\n'),
- onSave: codeSnippetWidget.updateMetadata,
+ onSave: codeSnippetWidget.updateMetadata
});
} else {
const selectedCells = getSelectedCellContents();
@@ -136,37 +137,38 @@ export const code_snippet_extension: JupyterFrontEndPlugin = {
schemaspace: CODE_SNIPPET_SCHEMASPACE,
schema: CODE_SNIPPET_SCHEMA,
code: code,
- onSave: codeSnippetWidget.updateMetadata,
+ onSave: codeSnippetWidget.updateMetadata
});
}
- },
+ }
});
app.contextMenu.addItem({
command: commandIDs.saveAsSnippet,
- selector: '.jp-Cell',
+ selector: '.jp-Cell'
});
app.contextMenu.addItem({
command: commandIDs.saveAsSnippet,
- selector: '.jp-FileEditor',
+ selector: '.jp-FileEditor'
});
app.contextMenu.addItem({
command: commandIDs.saveAsSnippet,
- selector: '.jp-MarkdownViewer',
+ selector: '.jp-MarkdownViewer'
});
const getTextSelection = (
- editor: any,
- markdownPreview?: boolean,
+ editor: CodeEditor.IEditor,
+ markdownPreview?: boolean
): string => {
const selectionObj = editor.getSelection();
const start = editor.getOffsetAt(selectionObj.start);
const end = editor.getOffsetAt(selectionObj.end);
- const selection = editor.model.value.text.substring(start, end);
+ const source = editor.model.sharedModel.getSource();
+ const selection = source.substring(start, end);
- if (!selection && editor.model.value.text) {
+ if (!selection && source) {
// Allow selections from a rendered notebook cell
return document.getSelection()?.toString() ?? '';
}
@@ -196,22 +198,24 @@ export const code_snippet_extension: JupyterFrontEndPlugin = {
return selectedCells;
};
- const isFileEditor = (currentWidget: any): boolean => {
+ const isFileEditor = (currentWidget: Widget | null): boolean => {
return (
currentWidget instanceof DocumentWidget &&
(currentWidget as DocumentWidget).content instanceof FileEditor
);
};
- const isNotebookEditor = (currentWidget: any): boolean => {
+ const isNotebookEditor = (currentWidget: Widget | null): boolean => {
return currentWidget instanceof NotebookPanel;
};
- const isMarkdownDocument = (currentWidget: any): boolean => {
+ const isMarkdownDocument = (currentWidget: Widget | null): boolean => {
return currentWidget instanceof MarkdownDocument;
};
- const getEditor = (currentWidget: any): any => {
+ const getEditor = (
+ currentWidget: Widget | null
+ ): CodeEditor.IEditor | null | undefined => {
if (isFileEditor(currentWidget)) {
const documentWidget = currentWidget as DocumentWidget;
return (documentWidget.content as FileEditor).editor;
@@ -220,8 +224,9 @@ export const code_snippet_extension: JupyterFrontEndPlugin = {
const notebookCell = (notebookWidget.content as Notebook).activeCell;
return notebookCell?.editor;
}
+ return undefined;
};
- },
+ }
};
export default code_snippet_extension;
diff --git a/packages/code-viewer/package.json b/packages/code-viewer/package.json
index 805d1a008..a0572fc76 100644
--- a/packages/code-viewer/package.json
+++ b/packages/code-viewer/package.json
@@ -25,7 +25,7 @@
"style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}"
],
"scripts": {
- "build": "jlpm run build:lib && jlpm run build:labextension:dev",
+ "build": "echo Skip the build until the extension is migrated to JL4",
"build:prod": "jlpm run build:lib && jlpm run build:labextension",
"build:lib": "tsc",
"build:labextension": "jupyter labextension build .",
diff --git a/packages/code-viewer/src/CodeViewerWidget.ts b/packages/code-viewer/src/CodeViewerWidget.ts
index 99ccb66dc..2bba4dc59 100644
--- a/packages/code-viewer/src/CodeViewerWidget.ts
+++ b/packages/code-viewer/src/CodeViewerWidget.ts
@@ -27,7 +27,7 @@ export class CodeViewerWidget extends Widget {
const editorWidget = new CodeEditorWrapper({
factory: options.factory,
- model: options.model,
+ model: options.model
});
this.editor = editorWidget.editor;
this.editor.setOption('readOnly', true);
@@ -37,7 +37,7 @@ export class CodeViewerWidget extends Widget {
}
static getCodeViewer(
- options: CodeViewerWidget.INoModelOptions,
+ options: CodeViewerWidget.INoModelOptions
): CodeViewerWidget {
const model = new CodeEditor.Model({ mimeType: options.mimeType });
model.sharedModel.source = options.content;
diff --git a/packages/code-viewer/src/index.ts b/packages/code-viewer/src/index.ts
index 549eee55e..c2259b215 100644
--- a/packages/code-viewer/src/index.ts
+++ b/packages/code-viewer/src/index.ts
@@ -17,7 +17,7 @@
import {
ILayoutRestorer,
JupyterFrontEnd,
- JupyterFrontEndPlugin,
+ JupyterFrontEndPlugin
} from '@jupyterlab/application';
import { MainAreaWidget, WidgetTracker } from '@jupyterlab/apputils';
import { CodeEditor, IEditorServices } from '@jupyterlab/codeeditor';
@@ -32,7 +32,7 @@ const ELYRA_CODE_VIEWER_NAMESPACE = 'elyra-code-viewer-extension';
* The command IDs used by the code-viewer plugin.
*/
const CommandIDs = {
- openViewer: 'elyra-code-viewer:open',
+ openViewer: 'elyra-code-viewer:open'
};
/**
@@ -46,12 +46,12 @@ const extension: JupyterFrontEndPlugin = {
activate: (
app: JupyterFrontEnd,
editorServices: IEditorServices,
- restorer: ILayoutRestorer,
+ restorer: ILayoutRestorer
) => {
console.log('Elyra - code-viewer extension is activated!');
const tracker = new WidgetTracker>({
- namespace: ELYRA_CODE_VIEWER_NAMESPACE,
+ namespace: ELYRA_CODE_VIEWER_NAMESPACE
});
// Handle state restoration
@@ -62,9 +62,9 @@ const extension: JupyterFrontEndPlugin = {
content: widget.content.getContent(),
label: widget.content.title.label,
mimeType: widget.content.getMimeType(),
- widgetId: widget.content.id,
+ widgetId: widget.content.id
}),
- name: (widget) => widget.content.id,
+ name: (widget) => widget.content.id
});
}
@@ -84,14 +84,14 @@ const extension: JupyterFrontEndPlugin = {
let mimetype = args.mimeType;
if (!mimetype && args.extension) {
mimetype = editorServices.mimeTypeService.getMimeTypeByFilePath(
- `temp.${args.extension.replace(/\\.$/, '')}`,
+ `temp.${args.extension.replace(/\\.$/, '')}`
);
}
const widget = CodeViewerWidget.getCodeViewer({
factory,
content: args.content,
- mimeType: mimetype,
+ mimeType: mimetype
});
widget.title.label = args.label || 'Code Viewer';
widget.title.caption = widget.title.label;
@@ -114,9 +114,9 @@ const extension: JupyterFrontEndPlugin = {
app.commands.addCommand(CommandIDs.openViewer, {
execute: (args: any) => {
return openCodeViewer(args);
- },
+ }
});
- },
+ }
};
export default extension;
diff --git a/packages/metadata-common/install.json b/packages/metadata-common/install.json
new file mode 100644
index 000000000..87e6c4e75
--- /dev/null
+++ b/packages/metadata-common/install.json
@@ -0,0 +1,5 @@
+{
+ "packageManager": "python",
+ "packageName": "elyra_metadata_common",
+ "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package elyra_metadata_common"
+}
diff --git a/packages/metadata-common/package.json b/packages/metadata-common/package.json
index 738d2c150..04148d435 100644
--- a/packages/metadata-common/package.json
+++ b/packages/metadata-common/package.json
@@ -24,21 +24,39 @@
"style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}"
],
"scripts": {
- "build": "tsc",
- "clean": "rimraf lib",
- "dist": "npm pack .",
- "prepare": "npm run build",
- "watch": "tsc -w",
- "lab:install": "jupyter labextension link --no-build",
- "lab:uninstall": "jupyter labextension unlink --no-build"
+ "build": "jlpm build:lib && jlpm build:labextension:dev",
+ "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension",
+ "build:labextension": "jupyter labextension build .",
+ "build:labextension:dev": "jupyter labextension build --development True .",
+ "build:lib": "tsc --sourceMap",
+ "build:lib:prod": "tsc",
+ "clean": "jlpm clean:lib",
+ "clean:lib": "rimraf lib tsconfig.tsbuildinfo",
+ "clean:lintcache": "rimraf .eslintcache .stylelintcache",
+ "clean:labextension": "rimraf ../../../../labextensions/elyra_metadata_common/labextension ../../../../labextensions/elyra_metadata_common/_version.py",
+ "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache",
+ "eslint": "jlpm eslint:check --fix",
+ "eslint:check": "eslint . --cache --ext .ts,.tsx",
+ "install:extension": "jlpm build",
+ "lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
+ "lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
+ "prettier": "jlpm prettier:base --write --list-different",
+ "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
+ "prettier:check": "jlpm prettier:base --check",
+ "stylelint": "jlpm stylelint:check --fix",
+ "stylelint:check": "stylelint --cache \"style/**/*.css\"",
+ "test": "jest --coverage --passWithNoTests",
+ "watch": "run-p watch:src watch:labextension",
+ "watch:src": "tsc -w --sourceMap",
+ "watch:labextension": "jupyter labextension watch ."
},
"dependencies": {
"@elyra/services": "4.0.0-dev",
"@elyra/ui-components": "4.0.0-dev",
- "@jupyterlab/application": "^4.0.6",
- "@jupyterlab/apputils": "^4.1.6",
- "@jupyterlab/codeeditor": "^4.0.6",
- "@jupyterlab/ui-components": "^4.0.6",
+ "@jupyterlab/application": "^4.2.5",
+ "@jupyterlab/apputils": "^4.2.5",
+ "@jupyterlab/codeeditor": "^4.2.5",
+ "@jupyterlab/ui-components": "^4.2.5",
"@lumino/algorithm": "*",
"@lumino/messaging": "^2.0.1",
"@lumino/widgets": "^2.3.1",
@@ -46,6 +64,7 @@
"react-dom": "^18.0.9"
},
"devDependencies": {
+ "@jupyterlab/builder": "^4.2.5",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"rimraf": "~5.0.5",
@@ -56,6 +75,6 @@
},
"jupyterlab": {
"extension": false,
- "outputDir": "../../build/labextensions/@elyra/metadata-common"
+ "outputDir": "../../labextensions/elyra_metadata_common/labextension"
}
}
diff --git a/packages/metadata-common/setup.py b/packages/metadata-common/setup.py
new file mode 100644
index 000000000..aefdf20db
--- /dev/null
+++ b/packages/metadata-common/setup.py
@@ -0,0 +1 @@
+__import__("setuptools").setup()
diff --git a/packages/metadata-common/src/FilterTools.tsx b/packages/metadata-common/src/FilterTools.tsx
index 65e95425c..8deee092a 100644
--- a/packages/metadata-common/src/FilterTools.tsx
+++ b/packages/metadata-common/src/FilterTools.tsx
@@ -61,7 +61,7 @@ export class FilterTools extends React.Component<
componentDidMount(): void {
this.setState({
selectedTags: [],
- searchValue: '',
+ searchValue: ''
});
}
@@ -70,14 +70,14 @@ export class FilterTools extends React.Component<
this.setState((state) => ({
selectedTags: state.selectedTags
.filter((tag) => this.props.tags.includes(tag))
- .sort(),
+ .sort()
}));
}
}
createFilterBox(): void {
const filterOption = document.querySelector(
- `#${this.props.schemaspace} .${FILTER_OPTION}`,
+ `#${this.props.schemaspace} .${FILTER_OPTION}`
);
filterOption?.classList.toggle('idle');
@@ -109,7 +109,7 @@ export class FilterTools extends React.Component<
renderAppliedTag(tag: string, index: string): JSX.Element {
return (
({
- selectedTags: this.updateTagsCss(
- target,
- state.selectedTags,
- clickedTag,
- ),
+ selectedTags: this.updateTagsCss(target, state.selectedTags, clickedTag)
}),
- this.filterMetadata,
+ this.filterMetadata
);
}
updateTagsCss(
target: HTMLElement,
currentTags: string[],
- clickedTag: string,
+ clickedTag: string
): string[] {
- if (target.classList.contains('unapplied-tag')) {
- target.classList.replace('unapplied-tag', 'applied-tag');
+ if (target.classList.contains('jp-CellTags-Unapplied')) {
+ target.classList.replace('jp-CellTags-Unapplied', 'jp-CellTags-Applied');
currentTags.splice(-1, 0, clickedTag);
- } else if (target.classList.contains('applied-tag')) {
- target.classList.replace('applied-tag', 'unapplied-tag');
+ } else if (target.classList.contains('jp-CellTags-Applied')) {
+ target.classList.replace('jp-CellTags-Applied', 'jp-CellTags-Unapplied');
const idx = currentTags.indexOf(clickedTag);
currentTags.splice(idx, 1);
@@ -187,7 +183,7 @@ export class FilterTools extends React.Component<
?.classList.contains('idle');
this.props.onFilter(
this.state.searchValue,
- isTagFilterOpen ? [] : this.state.selectedTags,
+ isTagFilterOpen ? [] : this.state.selectedTags
);
}
diff --git a/packages/metadata-common/src/MetadataCommonService.tsx b/packages/metadata-common/src/MetadataCommonService.tsx
index 2b392d57d..0b742e69f 100644
--- a/packages/metadata-common/src/MetadataCommonService.tsx
+++ b/packages/metadata-common/src/MetadataCommonService.tsx
@@ -32,7 +32,7 @@ export class MetadataCommonService {
static duplicateMetadataInstance(
schemaSpace: string,
metadataInstance: IMetadata,
- existingInstances: IMetadata[],
+ existingInstances: IMetadata[]
): Promise {
// iterate through the list of currently defined
// instance names and find the next available one
@@ -46,7 +46,7 @@ export class MetadataCommonService {
while (
existingInstances.find(
- (element) => element.display_name === `${base_name}-Copy${count}`,
+ (element) => element.display_name === `${base_name}-Copy${count}`
) !== undefined
) {
count += 1;
@@ -58,7 +58,7 @@ export class MetadataCommonService {
delete duplicated_metadata.name;
return MetadataService.postMetadata(
schemaSpace,
- JSON.stringify(duplicated_metadata),
+ JSON.stringify(duplicated_metadata)
);
}
}
diff --git a/packages/metadata-common/src/MetadataEditor.tsx b/packages/metadata-common/src/MetadataEditor.tsx
index 75472d30a..b5ec08e18 100644
--- a/packages/metadata-common/src/MetadataEditor.tsx
+++ b/packages/metadata-common/src/MetadataEditor.tsx
@@ -75,7 +75,7 @@ export const MetadataEditor: React.FC = ({
allTags,
componentRegistry,
getDefaultChoices,
- titleContext,
+ titleContext
}: IMetadataEditorComponentProps) => {
const [invalidForm, setInvalidForm] = React.useState(name === undefined);
@@ -96,7 +96,7 @@ export const MetadataEditor: React.FC = ({
const newMetadata: any = {
schema_name: schemaName,
display_name: metadata?.['_noCategory']?.['display_name'],
- metadata: flattenFormData(metadata),
+ metadata: flattenFormData(metadata)
};
if (!name) {
@@ -111,7 +111,7 @@ export const MetadataEditor: React.FC = ({
MetadataService.putMetadata(
schemaspace,
name,
- JSON.stringify(newMetadata),
+ JSON.stringify(newMetadata)
)
.then((response: any): void => {
setDirty(false);
@@ -146,7 +146,7 @@ export const MetadataEditor: React.FC = ({
* Triggers save and close on pressing enter key (outside of a text area)
*/
const onKeyPress: React.KeyboardEventHandler = (
- event: React.KeyboardEvent,
+ event: React.KeyboardEvent
) => {
const targetElement = event.nativeEvent.target as HTMLElement;
if (event.key === 'Enter' && targetElement?.tagName !== 'TEXTAREA') {
diff --git a/packages/metadata-common/src/MetadataEditorWidget.tsx b/packages/metadata-common/src/MetadataEditorWidget.tsx
index efc7bf453..4fb376e4a 100644
--- a/packages/metadata-common/src/MetadataEditorWidget.tsx
+++ b/packages/metadata-common/src/MetadataEditorWidget.tsx
@@ -121,7 +121,7 @@ export class MetadataEditorWidget extends ReactWidget {
// Load all schema and all metadata in schemaspace.
const allSchema = await MetadataService.getSchema(this.props.schemaspace);
const allMetadata = (this.allMetadata = await MetadataService.getMetadata(
- this.props.schemaspace,
+ this.props.schemaspace
));
// Loads all tags to display as options in the editor.
@@ -130,7 +130,7 @@ export class MetadataEditorWidget extends ReactWidget {
acc.push(
...metadata.metadata.tags.filter((tag: string) => {
return !acc.includes(tag);
- }),
+ })
);
}
return acc;
@@ -152,7 +152,7 @@ export class MetadataEditorWidget extends ReactWidget {
if (properties[prop].const !== undefined) {
properties[prop].default = properties[prop].const;
properties[prop].uihints = {
- 'ui:readonly': true,
+ 'ui:readonly': true
};
}
}
@@ -169,13 +169,13 @@ export class MetadataEditorWidget extends ReactWidget {
display_name: {
title: this.props.translator.__('Display Name'),
description: this.props.translator.__(
- 'Name used to identify an instance of metadata.',
+ 'Name used to identify an instance of metadata.'
),
- type: 'string',
- },
+ type: 'string'
+ }
},
- required: ['display_name'],
- },
+ required: ['display_name']
+ }
};
// Adds required fields to the wrapper required fields.
@@ -198,7 +198,7 @@ export class MetadataEditorWidget extends ReactWidget {
schemaPropertiesByCategory[category] = {
type: 'object',
properties: {},
- required: [],
+ required: []
};
}
if (schema.properties.metadata.required?.includes(schemaProperty)) {
@@ -236,7 +236,7 @@ export class MetadataEditorWidget extends ReactWidget {
if (!isFocused) {
const input = document.querySelector(
- `.${this.widgetClass} .display_nameField input`,
+ `.${this.widgetClass} .display_nameField input`
) as HTMLInputElement;
if (input) {
input.focus();
@@ -278,7 +278,7 @@ export class MetadataEditorWidget extends ReactWidget {
showDialog({
title: this.props.translator.__('Close without saving?'),
body: Metadata has unsaved changes, close without saving?
,
- buttons: [Dialog.cancelButton(), Dialog.okButton()],
+ buttons: [Dialog.cancelButton(), Dialog.okButton()]
}).then((response: any): void => {
if (response.button.accept) {
this.dispose();
diff --git a/packages/metadata-common/src/MetadataWidget.tsx b/packages/metadata-common/src/MetadataWidget.tsx
index 1ffe9980d..7099f1039 100644
--- a/packages/metadata-common/src/MetadataWidget.tsx
+++ b/packages/metadata-common/src/MetadataWidget.tsx
@@ -19,7 +19,7 @@ import {
ExpandableComponent,
JSONComponent,
RequestErrors,
- trashIcon,
+ trashIcon
} from '@elyra/ui-components';
import { JupyterFrontEnd } from '@jupyterlab/application';
@@ -27,14 +27,14 @@ import {
Dialog,
ReactWidget,
showDialog,
- UseSignal,
+ UseSignal
} from '@jupyterlab/apputils';
import {
addIcon,
copyIcon,
editIcon,
LabIcon,
- refreshIcon,
+ refreshIcon
} from '@jupyterlab/ui-components';
import { Message } from '@lumino/messaging';
import { Signal } from '@lumino/signaling';
@@ -54,7 +54,7 @@ const METADATA_HEADER_BUTTON_CLASS = 'elyra-metadataHeader-button';
const METADATA_JSON_CLASS = 'jp-RenderedJSON CodeMirror cm-s-jupyter';
const commands = {
- OPEN_METADATA_EDITOR: 'elyra-metadata-editor:open',
+ OPEN_METADATA_EDITOR: 'elyra-metadata-editor:open'
};
export interface IMetadata {
@@ -101,7 +101,7 @@ export interface IMetadataDisplayState {
* A React Component for displaying a list of metadata
*/
export class MetadataDisplay<
- T extends IMetadataDisplayProps,
+ T extends IMetadataDisplayProps
// U extends IMetadataDisplayState,
> extends React.Component {
constructor(props: T) {
@@ -111,7 +111,7 @@ export class MetadataDisplay<
searchValue: '',
filterTags: [],
matchesSearch: this.matchesSearch.bind(this),
- matchesTags: this.matchesTags.bind(this),
+ matchesTags: this.matchesTags.bind(this)
};
}
@@ -120,13 +120,13 @@ export class MetadataDisplay<
title: `Delete ${
this.props.labelName ? this.props.labelName(metadata) : ''
} ${this.props.titleContext || ''} '${metadata.display_name}'?`,
- buttons: [Dialog.cancelButton(), Dialog.okButton()],
+ buttons: [Dialog.cancelButton(), Dialog.okButton()]
}).then((result: any) => {
// Do nothing if the cancel button is pressed
if (result.button.accept) {
MetadataService.deleteMetadata(
this.props.schemaspace,
- metadata.name,
+ metadata.name
).catch((error) => RequestErrors.serverError(error));
}
});
@@ -142,9 +142,9 @@ export class MetadataDisplay<
onSave: this.props.updateMetadata,
schemaspace: this.props.schemaspace,
schema: metadata.schema_name,
- name: metadata.name,
+ name: metadata.name
});
- },
+ }
},
{
title: 'Duplicate',
@@ -153,13 +153,13 @@ export class MetadataDisplay<
MetadataCommonService.duplicateMetadataInstance(
this.props.schemaspace,
metadata,
- this.props.metadata,
+ this.props.metadata
)
.then((response: any): void => {
this.props.updateMetadata();
})
.catch((error) => RequestErrors.serverError(error));
- },
+ }
},
{
title: 'Delete',
@@ -168,8 +168,8 @@ export class MetadataDisplay<
this.deleteMetadata(metadata).then((response: any): void => {
this.props.updateMetadata();
});
- },
- },
+ }
+ }
];
}
@@ -214,7 +214,7 @@ export class MetadataDisplay<
*/
sortMetadata(): void {
this.props.metadata.sort((a, b) =>
- a.display_name.localeCompare(b.display_name),
+ a.display_name.localeCompare(b.display_name)
);
}
@@ -228,7 +228,7 @@ export class MetadataDisplay<
.toLowerCase()
.includes(searchValue.toLowerCase())
);
- },
+ }
);
// filter with tags
@@ -246,7 +246,7 @@ export class MetadataDisplay<
this.setState({
metadata: filteredMetadata,
searchValue: searchValue,
- filterTags: filterTags,
+ filterTags: filterTags
});
};
@@ -286,7 +286,7 @@ export class MetadataDisplay<
static getDerivedStateFromProps(
props: IMetadataDisplayProps,
- state: IMetadataDisplayState,
+ state: IMetadataDisplayState
): IMetadataDisplayState {
if (state.searchValue === '' && state.filterTags.length === 0) {
return {
@@ -294,7 +294,7 @@ export class MetadataDisplay<
searchValue: '',
filterTags: [],
matchesSearch: state.matchesSearch,
- matchesTags: state.matchesTags,
+ matchesTags: state.matchesTags
};
}
@@ -305,14 +305,14 @@ export class MetadataDisplay<
const newMetadata = props.metadata.filter(
(metadata) =>
state.matchesSearch(searchValue, metadata) &&
- state.matchesTags(filterTags, metadata),
+ state.matchesTags(filterTags, metadata)
);
return {
metadata: newMetadata,
searchValue: state.searchValue,
filterTags: state.filterTags,
matchesSearch: state.matchesSearch,
- matchesTags: state.matchesTags,
+ matchesTags: state.matchesTags
};
}
return state;
@@ -346,6 +346,7 @@ export interface IMetadataWidgetProps {
icon: LabIcon;
titleContext?: string;
appendToTitle?: boolean;
+ addLabel?: string;
}
/**
@@ -356,6 +357,7 @@ export class MetadataWidget extends ReactWidget {
props: IMetadataWidgetProps;
schemas?: IDictionary[];
titleContext?: string;
+ addLabel?: string;
refreshButtonTooltip?: string;
constructor(props: IMetadataWidgetProps) {
@@ -365,6 +367,7 @@ export class MetadataWidget extends ReactWidget {
this.props = props;
this.renderSignal = new Signal(this);
this.titleContext = props.titleContext;
+ this.addLabel = props.addLabel;
this.fetchMetadata = this.fetchMetadata.bind(this);
this.getSchemas = this.getSchemas.bind(this);
this.updateMetadata = this.updateMetadata.bind(this);
@@ -392,8 +395,9 @@ export class MetadataWidget extends ReactWidget {
schema: schema.name,
title: schema.title,
titleContext: this.props.titleContext,
- appendToTitle: this.props.appendToTitle,
- } as any,
+ addLabel: this.props.addLabel,
+ appendToTitle: this.props.appendToTitle
+ } as any
});
}
}
@@ -408,7 +412,7 @@ export class MetadataWidget extends ReactWidget {
onSave: this.updateMetadata,
schemaspace: this.props.schemaspace,
schema: schema,
- titleContext,
+ titleContext
});
}
@@ -525,13 +529,13 @@ export class MetadataWidget extends ReactWidget {
? (): void =>
this.addMetadata(
this.schemas?.[0].name,
- this.titleContext,
+ this.titleContext
)
: (event: any): void => {
this.props.app.contextMenu.open(event);
}
}
- title={`Create new ${this.titleContext}`}
+ title={`Create new ${this.addLabel}`}
>
diff --git a/packages/metadata/install.json b/packages/metadata/install.json
new file mode 100644
index 000000000..4aa136ba4
--- /dev/null
+++ b/packages/metadata/install.json
@@ -0,0 +1,5 @@
+{
+ "packageManager": "python",
+ "packageName": "elyra_metadata_extension",
+ "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package elyra_metadata_extension"
+}
diff --git a/packages/metadata/package.json b/packages/metadata/package.json
index b9fb0e89c..5ad318f51 100644
--- a/packages/metadata/package.json
+++ b/packages/metadata/package.json
@@ -25,31 +25,40 @@
"style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}"
],
"scripts": {
- "build": "jlpm run build:lib && jlpm run build:labextension:dev",
- "build:prod": "jlpm run build:lib && jlpm run build:labextension",
- "build:lib": "tsc",
+ "build": "jlpm build:lib && jlpm build:labextension:dev",
+ "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension",
"build:labextension": "jupyter labextension build .",
"build:labextension:dev": "jupyter labextension build --development True .",
- "clean": "rimraf lib tsconfig.tsbuildinfo ../../build/labextensions/@elyra/metadata-extension",
- "lab:dev": "jupyter labextension develop --overwrite ../../build/labextensions/@elyra/metadata-extension",
- "dist": "npm pack .",
- "prepare": "npm run build",
+ "build:lib": "tsc --sourceMap",
+ "build:lib:prod": "tsc",
+ "clean": "jlpm clean:lib",
+ "clean:lib": "rimraf lib tsconfig.tsbuildinfo",
+ "clean:lintcache": "rimraf .eslintcache .stylelintcache",
+ "clean:labextension": "rimraf ../../../../labextensions/elyra_metadata_extension/labextension ../../../../labextensions/elyra_metadata_extension/_version.py",
+ "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache",
+ "eslint": "jlpm eslint:check --fix",
+ "eslint:check": "eslint . --cache --ext .ts,.tsx",
+ "install:extension": "jlpm build",
+ "lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
+ "lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
+ "prettier": "jlpm prettier:base --write --list-different",
+ "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
+ "prettier:check": "jlpm prettier:base --check",
+ "stylelint": "jlpm stylelint:check --fix",
+ "stylelint:check": "stylelint --cache \"style/**/*.css\"",
+ "test": "jest --coverage --passWithNoTests",
"watch": "run-p watch:src watch:labextension",
- "watch:src": "tsc -w",
- "watch:labextension": "jupyter labextension watch .",
- "lab:install": "jupyter labextension install --no-build",
- "lab:uninstall": "jupyter labextension uninstall --no-build",
- "link:dev": "yarn link @jupyterlab/builder",
- "unlink:dev": "yarn unlink @jupyterlab/builder"
+ "watch:src": "tsc -w --sourceMap",
+ "watch:labextension": "jupyter labextension watch ."
},
"dependencies": {
"@elyra/metadata-common": "4.0.0-dev",
"@elyra/services": "4.0.0-dev",
- "@jupyterlab/application": "^4.0.6",
- "@jupyterlab/apputils": "^4.1.6",
- "@jupyterlab/builder": "^4.0.6",
- "@jupyterlab/codeeditor": "^4.0.6",
- "@jupyterlab/ui-components": "^4.0.6",
+ "@jupyterlab/application": "^4.2.5",
+ "@jupyterlab/apputils": "^4.2.5",
+ "@jupyterlab/builder": "^4.2.5",
+ "@jupyterlab/codeeditor": "^4.2.5",
+ "@jupyterlab/ui-components": "^4.2.5",
"@lumino/algorithm": "*",
"@lumino/widgets": "^2.3.1"
},
@@ -62,6 +71,6 @@
},
"jupyterlab": {
"extension": true,
- "outputDir": "../../build/labextensions/@elyra/metadata-extension"
+ "outputDir": "../../labextensions/elyra_metadata_extension/labextension"
}
}
diff --git a/packages/metadata/setup.py b/packages/metadata/setup.py
new file mode 100644
index 000000000..aefdf20db
--- /dev/null
+++ b/packages/metadata/setup.py
@@ -0,0 +1 @@
+__import__("setuptools").setup()
diff --git a/packages/metadata/src/index.ts b/packages/metadata/src/index.ts
index dba5d1576..e733dc950 100644
--- a/packages/metadata/src/index.ts
+++ b/packages/metadata/src/index.ts
@@ -18,16 +18,16 @@ import { MetadataWidget, MetadataEditorWidget } from '@elyra/metadata-common';
import { MetadataService } from '@elyra/services';
import {
- DropDown,
RequestErrors,
+ PasswordField,
CodeBlock,
TagsField,
- PasswordField,
+ DropDown
} from '@elyra/ui-components';
import {
JupyterFrontEnd,
JupyterFrontEndPlugin,
- ILabStatus,
+ ILabStatus
} from '@jupyterlab/application';
import { ICommandPalette, MainAreaWidget } from '@jupyterlab/apputils';
import { IEditorServices } from '@jupyterlab/codeeditor';
@@ -35,8 +35,7 @@ import { ITranslator } from '@jupyterlab/translation';
import {
textEditorIcon,
LabIcon,
- IFormRendererRegistry,
- IFormRenderer,
+ IFormRendererRegistry
} from '@jupyterlab/ui-components';
import { find } from '@lumino/algorithm';
@@ -44,10 +43,11 @@ import { Widget } from '@lumino/widgets';
const METADATA_EDITOR_ID = 'elyra-metadata-editor';
const METADATA_WIDGET_ID = 'elyra-metadata';
+const METADATA_EXTENSION_ID = '@elyra/metadata-extension';
const commandIDs = {
openMetadata: 'elyra-metadata:open',
- closeTabCommand: 'elyra-metadata:close',
+ closeTabCommand: 'elyra-metadata:close'
};
/**
@@ -61,7 +61,7 @@ const extension: JupyterFrontEndPlugin = {
IEditorServices,
ILabStatus,
IFormRendererRegistry,
- ITranslator,
+ ITranslator
],
activate: async (
app: JupyterFrontEnd,
@@ -69,26 +69,30 @@ const extension: JupyterFrontEndPlugin = {
editorServices: IEditorServices,
status: ILabStatus,
componentRegistry: IFormRendererRegistry,
- translator: ITranslator,
+ translator: ITranslator
) => {
console.log('Elyra - metadata extension is activated!');
- componentRegistry.addRenderer(
- 'code',
- CodeBlock as unknown as IFormRenderer,
- );
- componentRegistry.addRenderer(
- 'tags',
- TagsField as unknown as IFormRenderer,
- );
- componentRegistry.addRenderer(
- 'dropdown',
- DropDown as unknown as IFormRenderer,
- );
- componentRegistry.addRenderer(
- 'password',
- PasswordField as unknown as IFormRenderer,
- );
+ componentRegistry.addRenderer(`${METADATA_EXTENSION_ID}:plugin.code`, {
+ fieldRenderer: (props) => {
+ return CodeBlock(props);
+ }
+ });
+ componentRegistry.addRenderer(`${METADATA_EXTENSION_ID}:plugin.tags`, {
+ fieldRenderer: (props) => {
+ return TagsField(props);
+ }
+ });
+ componentRegistry.addRenderer(`${METADATA_EXTENSION_ID}:plugin.dropdown`, {
+ fieldRenderer: (props) => {
+ return DropDown(props);
+ }
+ });
+ componentRegistry.addRenderer(`${METADATA_EXTENSION_ID}:plugin.password`, {
+ fieldRenderer: (props) => {
+ return PasswordField(props);
+ }
+ });
const openMetadataEditor = (args: {
schema: string;
@@ -110,7 +114,7 @@ const extension: JupyterFrontEndPlugin = {
app.shell.widgets('main'),
(widget: Widget, index: number) => {
return widget.id === widgetId;
- },
+ }
);
if (openWidget) {
app.shell.activateById(widgetId);
@@ -123,7 +127,7 @@ const extension: JupyterFrontEndPlugin = {
editorServices,
status,
translator: translator.load('jupyterlab'),
- componentRegistry,
+ componentRegistry
});
const main = new MainAreaWidget({ content: metadataEditorWidget });
main.title.label = widgetLabel;
@@ -142,13 +146,14 @@ const extension: JupyterFrontEndPlugin = {
},
execute: (args: any) => {
openMetadataEditor(args);
- },
+ }
});
const openMetadataWidget = (args: {
display_name: string;
schemaspace: string;
icon: string;
+ addLabel?: string;
}): void => {
const labIcon = LabIcon.resolve({ icon: args.icon });
const widgetId = `${METADATA_WIDGET_ID}:${args.schemaspace}`;
@@ -157,6 +162,7 @@ const extension: JupyterFrontEndPlugin = {
display_name: args.display_name,
schemaspace: args.schemaspace,
icon: labIcon,
+ addLabel: args.addLabel
});
metadataWidget.id = widgetId;
metadataWidget.title.icon = labIcon;
@@ -178,7 +184,7 @@ const extension: JupyterFrontEndPlugin = {
// Rank has been chosen somewhat arbitrarily to give priority
// to the running sessions widget in the sidebar.
openMetadataWidget(args);
- },
+ }
});
// Add command to close metadata tab
@@ -187,7 +193,7 @@ const extension: JupyterFrontEndPlugin = {
label: 'Close Tab',
execute: (args) => {
const contextNode: HTMLElement | undefined = app.contextMenuHitTest(
- (node) => !!node.dataset.id,
+ (node) => !!node.dataset.id
);
if (contextNode) {
const id = contextNode.dataset['id']!;
@@ -195,18 +201,18 @@ const extension: JupyterFrontEndPlugin = {
app.shell.widgets('left'),
(widget: Widget, index: number) => {
return widget.id === id;
- },
+ }
);
if (widget) {
widget.dispose();
}
}
- },
+ }
});
app.contextMenu.addItem({
selector:
'[data-id^="elyra-metadata:"]:not([data-id$="code-snippets"]):not([data-id$="runtimes"])',
- command: closeTabCommand,
+ command: closeTabCommand
});
try {
@@ -228,15 +234,15 @@ const extension: JupyterFrontEndPlugin = {
label: `Manage ${title}`,
display_name: schema.uihints.title,
schemaspace: schema.schemaspace,
- icon: icon,
+ icon: icon
},
- category: 'Elyra',
+ category: 'Elyra'
});
}
} catch (error) {
RequestErrors.serverError(error);
}
- },
+ }
};
export default extension;
diff --git a/packages/pipeline-editor/install.json b/packages/pipeline-editor/install.json
new file mode 100644
index 000000000..7a6828db9
--- /dev/null
+++ b/packages/pipeline-editor/install.json
@@ -0,0 +1,5 @@
+{
+ "packageManager": "python",
+ "packageName": "elyra_pipeline_editor_extension",
+ "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package elyra_pipeline_editor_extension"
+}
diff --git a/packages/pipeline-editor/package.json b/packages/pipeline-editor/package.json
index 772eb8b4a..36242ab67 100644
--- a/packages/pipeline-editor/package.json
+++ b/packages/pipeline-editor/package.json
@@ -22,27 +22,35 @@
"files": [
"lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
"src/**/*.{ts,tsx}",
- "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}"
+ "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
+ "schema/*.json"
],
"scripts": {
- "build:test": "tsc --build tsconfig.test.json",
- "build": "jlpm run build:lib && jlpm run build:labextension:dev",
- "build:prod": "jlpm run build:lib && jlpm run build:labextension",
- "build:lib": "tsc",
+ "build": "jlpm build:lib && jlpm build:labextension:dev",
+ "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension",
"build:labextension": "jupyter labextension build .",
"build:labextension:dev": "jupyter labextension build --development True .",
- "clean": "rimraf lib tsconfig.tsbuildinfo ../../build/labextensions/@elyra/metadata-extension",
- "lab:dev": "jupyter labextension develop --overwrite ../../build/labextensions/@elyra/metadata-extension",
- "dist": "npm pack .",
- "prepare": "npm run build ",
- "test": "FORCE_COLOR=true jest",
+ "build:lib": "tsc --sourceMap",
+ "build:lib:prod": "tsc",
+ "clean": "jlpm clean:lib",
+ "clean:lib": "rimraf lib tsconfig.tsbuildinfo",
+ "clean:lintcache": "rimraf .eslintcache .stylelintcache",
+ "clean:labextension": "rimraf ../../../../labextensions/elyra_pipeline_editor_extension/labextension ../../../../labextensions/elyra_pipeline_editor_extension/_version.py",
+ "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache",
+ "eslint": "jlpm eslint:check --fix",
+ "eslint:check": "eslint . --cache --ext .ts,.tsx",
+ "install:extension": "jlpm build",
+ "lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
+ "lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
+ "prettier": "jlpm prettier:base --write --list-different",
+ "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
+ "prettier:check": "jlpm prettier:base --check",
+ "stylelint": "jlpm stylelint:check --fix",
+ "stylelint:check": "stylelint --cache \"style/**/*.css\"",
+ "test": "jest --coverage --passWithNoTests",
"watch": "run-p watch:src watch:labextension",
- "watch:src": "tsc -w",
- "watch:labextension": "jupyter labextension watch .",
- "lab:install": "jupyter labextension install --no-build",
- "lab:uninstall": "jupyter labextension uninstall --no-build",
- "link:dev": "yarn link @jupyterlab/builder",
- "unlink:dev": "yarn unlink @jupyterlab/builder"
+ "watch:src": "tsc -w --sourceMap",
+ "watch:labextension": "jupyter labextension watch ."
},
"dependencies": {
"@elyra/metadata-common": "4.0.0-dev",
@@ -50,20 +58,20 @@
"@elyra/pipeline-services": "1.12.1",
"@elyra/services": "4.0.0-dev",
"@elyra/ui-components": "4.0.0-dev",
- "@jupyterlab/application": "^4.0.6",
- "@jupyterlab/apputils": "^4.1.6",
- "@jupyterlab/builder": "^4.0.6",
+ "@jupyterlab/application": "^4.2.5",
+ "@jupyterlab/apputils": "^4.2.5",
+ "@jupyterlab/builder": "^4.2.5",
"@jupyterlab/coreutils": "^6.0.6",
- "@jupyterlab/docregistry": "^4.0.6",
- "@jupyterlab/filebrowser": "^4.0.6",
- "@jupyterlab/filebrowser-extension": "^4.0.6",
- "@jupyterlab/fileeditor": "^4.0.6",
- "@jupyterlab/launcher": "^4.0.6",
- "@jupyterlab/mainmenu": "^4.0.6",
- "@jupyterlab/notebook": "^4.0.6",
- "@jupyterlab/outputarea": "^4.0.6",
+ "@jupyterlab/docregistry": "^4.2.5",
+ "@jupyterlab/filebrowser": "^4.2.5",
+ "@jupyterlab/filebrowser-extension": "^4.2.5",
+ "@jupyterlab/fileeditor": "^4.2.5",
+ "@jupyterlab/launcher": "^4.2.5",
+ "@jupyterlab/mainmenu": "^4.2.5",
+ "@jupyterlab/notebook": "^4.2.5",
+ "@jupyterlab/outputarea": "^4.2.5",
"@jupyterlab/services": "^7.0.6",
- "@jupyterlab/ui-components": "^4.0.6",
+ "@jupyterlab/ui-components": "^4.2.5",
"@lumino/algorithm": "*",
"@lumino/coreutils": "^2.1.2",
"@lumino/disposable": "^2.1.2",
@@ -84,17 +92,17 @@
"uuid": "^3.4.0"
},
"devDependencies": {
- "@types/jest": "^23.3.11",
+ "@types/jest": "^29.2.0",
"@types/node": "^12.0.10",
"@types/react": "18.0.26",
"@types/react-dom": "18.0.9",
"@types/uuid": "^3.4.7",
"cheerio": "^1.0.0-rc.3",
- "jest": "^24.7.1",
+ "jest": "^29.2.0",
"jest-raw-loader": "^1.0.1",
"rimraf": "~5.0.5",
"source-map-loader": "^0.2.4",
- "ts-jest": "^24.0.2",
+ "ts-jest": "^29.2.5",
"ts-loader": "^6.2.1",
"typescript": "~5.1.6"
},
@@ -107,6 +115,6 @@
"jupyterlab": {
"extension": true,
"schemaDir": "schema",
- "outputDir": "../../build/labextensions/@elyra/pipeline-editor-extension"
+ "outputDir": "../../labextensions/elyra_pipeline_editor_extension/labextension"
}
}
diff --git a/packages/pipeline-editor/schema/src/ComponentCatalogsWidget.tsx b/packages/pipeline-editor/schema/src/ComponentCatalogsWidget.tsx
deleted file mode 100644
index c934203d4..000000000
--- a/packages/pipeline-editor/schema/src/ComponentCatalogsWidget.tsx
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import {
- MetadataWidget,
- IMetadataWidgetProps,
- IMetadata,
- MetadataDisplay,
- IMetadataDisplayProps,
- //IMetadataDisplayState,
- IMetadataActionButton,
-} from '@elyra/metadata-common';
-import { IDictionary, MetadataService } from '@elyra/services';
-import { RequestErrors } from '@elyra/ui-components';
-import { JupyterFrontEnd } from '@jupyterlab/application';
-import { LabIcon, refreshIcon } from '@jupyterlab/ui-components';
-
-import React from 'react';
-
-import { IRuntimeType, PipelineService } from './PipelineService';
-
-export const COMPONENT_CATALOGS_SCHEMASPACE = 'component-catalogs';
-
-const COMPONENT_CATALOGS_CLASS = 'elyra-metadata-component-catalogs';
-
-const handleError = (error: any): void => {
- // silently eat a 409, the server will log in in the console
- if (error.status !== 409) {
- RequestErrors.serverError(error);
- }
-};
-
-/**
- * A React Component for displaying the component catalogs list.
- */
-class ComponentCatalogsDisplay extends MetadataDisplay {
- actionButtons(metadata: IMetadata): IMetadataActionButton[] {
- return [
- {
- title: 'Reload components from catalog',
- icon: refreshIcon,
- onClick: (): void => {
- PipelineService.refreshComponentsCache(metadata.name)
- .then((response: any): void => {
- this.props.updateMetadata();
- })
- .catch((error) =>
- console.error(
- 'An error occurred while refreshing components from catalog:',
- error,
- ),
- );
- },
- },
- ...super.actionButtons(metadata),
- ];
- }
-
- //render catalog entries
- renderExpandableContent(metadata: IDictionary): JSX.Element {
- let category_output = No category ;
- if (metadata.metadata.categories) {
- category_output = metadata.metadata.categories.map((category: string) => (
- {category}
- ));
- }
-
- return (
-
-
Runtime Type
- {metadata.metadata.runtime_type}
-
-
-
Description
- {metadata.metadata.description ?? 'No description'}
-
-
-
Categories
-
-
- );
- }
-
- // Allow for filtering by display_name, name, and description
- matchesSearch(searchValue: string, metadata: IMetadata): boolean {
- searchValue = searchValue.toLowerCase();
- // True if search string is in name or display_name,
- // or if the search string is empty
- const description = (metadata.metadata.description || '').toLowerCase();
- return (
- metadata.name.toLowerCase().includes(searchValue) ||
- metadata.display_name.toLowerCase().includes(searchValue) ||
- description.includes(searchValue)
- );
- }
-}
-
-/**
- * ComponentCatalogsWidget props.
- */
-export interface IComponentCatalogsWidgetProps extends IMetadataWidgetProps {
- app: JupyterFrontEnd;
- display_name: string;
- schemaspace: string;
- icon: LabIcon;
- titleContext?: string;
- appendToTitle?: boolean;
- refreshCallback?: () => void;
-}
-
-/**
- * A widget for displaying component catalogs.
- */
-export class ComponentCatalogsWidget extends MetadataWidget {
- refreshButtonTooltip: string;
- refreshCallback?: () => void;
- runtimeTypes: IRuntimeType[] = [];
-
- constructor(props: IComponentCatalogsWidgetProps) {
- super(props);
- this.refreshCallback = props.refreshCallback;
- this.refreshButtonTooltip =
- 'Refresh list and reload components from all catalogs';
- }
-
- async getSchemas(): Promise {
- try {
- const schemas = await MetadataService.getSchema(this.props.schemaspace);
- this.runtimeTypes = await PipelineService.getRuntimeTypes();
- const sortedSchema = schemas.sort((a: any, b: any) =>
- a.title.localeCompare(b.title),
- );
- this.schemas = sortedSchema.filter((schema: any) => {
- return !!this.runtimeTypes.find(
- (r) =>
- schema.properties?.metadata?.properties?.runtime_type?.enum?.includes(
- r.id,
- ) && r.runtime_enabled,
- );
- });
- if (this.schemas?.length ?? 0 > 1) {
- for (const schema of this.schemas ?? []) {
- this.props.app.contextMenu.addItem({
- selector: `#${this.props.schemaspace} .elyra-metadataHeader-addButton`,
- command: 'elyra-metadata-editor:open',
- args: {
- onSave: this.updateMetadata,
- schemaspace: this.props.schemaspace,
- schema: schema.name,
- title: schema.title,
- titleContext: this.props.titleContext,
- appendToTitle: this.props.appendToTitle,
- } as any,
- });
- }
- }
- this.update();
- } catch (error) {
- RequestErrors.serverError(error);
- }
- }
-
- // wrapper function that refreshes the palette after calling updateMetadata
- updateMetadataAndRefresh = (): void => {
- super.updateMetadata();
- if (this.refreshCallback) {
- this.refreshCallback();
- }
- };
-
- refreshMetadata(): void {
- PipelineService.refreshComponentsCache()
- .then((response: any): void => {
- this.updateMetadataAndRefresh();
- })
- .catch((error) => handleError(error));
- }
-
- renderDisplay(metadata: IMetadata[]): React.ReactElement {
- if (Array.isArray(metadata) && !metadata.length) {
- // Empty metadata
- return (
-
-
-
- Click the + button to add {this.props.display_name.toLowerCase()}
-
-
- );
- }
-
- const filteredMetadata = metadata.filter((m) => {
- return !!this.runtimeTypes.find((r) => m.metadata?.runtime_type === r.id);
- });
-
- return (
-
- );
- }
-}
diff --git a/packages/pipeline-editor/schema/src/EmptyPipelineContent.tsx b/packages/pipeline-editor/schema/src/EmptyPipelineContent.tsx
deleted file mode 100644
index 3af93d71d..000000000
--- a/packages/pipeline-editor/schema/src/EmptyPipelineContent.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { componentCatalogIcon, dragDropIcon } from '@elyra/ui-components';
-import { settingsIcon } from '@jupyterlab/ui-components';
-
-import React from 'react';
-
-const HEADER_CLASS = 'empty-pipeline-header';
-const BUTTON_CLASS = 'empty-pipeline-button';
-const ICON_CLASS = 'empty-pipeline-icon';
-
-export interface IEmptyGenericPipelineProps {
- onOpenSettings: () => void;
-}
-
-export const EmptyGenericPipeline: React.FC = ({
- onOpenSettings,
-}) => {
- return (
-
-
-
- Start your new pipeline by dragging files from the file browser pane
-
-
-
-
- Click{' '}
-
-
- {' '}
- to configure the pipeline editor.
-
-
- );
-};
-
-export interface IEmptyPlatformSpecificPipelineProps {
- onOpenCatalog: () => void;
- onOpenSettings: () => void;
-}
-
-export const EmptyPlatformSpecificPipeline: React.FC<
- IEmptyPlatformSpecificPipelineProps
-> = ({ onOpenCatalog, onOpenSettings }) => {
- // Note: the URL is rewritten by the release script by replacing `latest` with a
- // specific version number, e.g. https://.../en/v3.6.0/user_guide/pi...
- const customComponentsHelpTopicURL =
- 'https://elyra.readthedocs.io/en/latest/user_guide/pipeline-components.html';
-
- return (
-
-
-
- Start your new pipeline by dragging files from the file browser pane or
- add custom components by clicking the{' '}
-
-
- {' '}
- button.
-
-
-
-
-
- Click{' '}
-
-
- {' '}
- to configure the pipeline editor.
-
-
- );
-};
diff --git a/packages/pipeline-editor/schema/src/FileSubmissionDialog.tsx b/packages/pipeline-editor/schema/src/FileSubmissionDialog.tsx
deleted file mode 100644
index 978af1112..000000000
--- a/packages/pipeline-editor/schema/src/FileSubmissionDialog.tsx
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { IDictionary } from '@elyra/services';
-import * as React from 'react';
-
-import { IRuntimeData } from './runtime-utils';
-import RuntimeConfigSelect from './RuntimeConfigSelect';
-import Utils from './utils';
-
-interface IProps {
- env: string[];
- dependencyFileExtension: string;
- images: IDictionary;
- runtimeData: IRuntimeData;
-}
-
-const EnvForm: React.FC<{ env: string[] }> = ({ env }) => {
- if (env.length > 0) {
- return (
- <>
-
-
- Environment Variables:
-
- {Utils.chunkArray(env, 4).map((col, i) => (
-
- {col.map((envVar) => (
-
- {envVar}:
-
-
-
- ))}
-
- ))}
- >
- );
- }
- return null;
-};
-
-export const FileSubmissionDialog: React.FC = ({
- env,
- images,
- dependencyFileExtension,
- runtimeData,
-}) => {
- const [includeDependency, setIncludeDependency] = React.useState(true);
-
- const handleToggle = (): void => {
- setIncludeDependency((prev) => !prev);
- };
-
- return (
-
- );
-};
diff --git a/packages/pipeline-editor/schema/src/ParameterInputForm.tsx b/packages/pipeline-editor/schema/src/ParameterInputForm.tsx
deleted file mode 100644
index 45306a1f4..000000000
--- a/packages/pipeline-editor/schema/src/ParameterInputForm.tsx
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import React from 'react';
-
-const DIALOG_WIDTH = 27;
-
-export interface IParameterProps {
- parameters?: {
- name: string;
- default_value?: {
- type: 'String' | 'Integer' | 'Float' | 'Bool';
- value: any;
- };
- type?: string;
- required?: boolean;
- description?: string;
- }[];
-}
-
-export const ParameterInputForm: React.FC = ({
- parameters,
-}) => {
- return parameters && parameters.length > 0 ? (
-
-
- Parameters
-
- {parameters.map((param) => {
- if (!param.name) {
- return undefined;
- }
- const required =
- param.required === true && param.default_value?.value === ''
- ? true
- : undefined;
- let type = 'text';
- switch (param.default_value?.type) {
- case 'Bool':
- type = 'checkbox';
- break;
- case 'Float':
- case 'Integer':
- type = 'number';
- break;
- }
- if (type === 'checkbox') {
- return (
-
-
- {`${param.name}${
- required ? '*' : ''
- }`}
-
-
-
- );
- }
- return (
-
-
-
{`${param.name}${param.required ? '*' : ''}`}
- {param.description && (
-
-
?
-
- {param.description}
-
-
- )}
-
-
-
-
-
- );
- })}
-
- ) : (
-
- );
-};
diff --git a/packages/pipeline-editor/schema/src/PipelineEditorWidget.tsx b/packages/pipeline-editor/schema/src/PipelineEditorWidget.tsx
deleted file mode 100644
index 4e8a37b47..000000000
--- a/packages/pipeline-editor/schema/src/PipelineEditorWidget.tsx
+++ /dev/null
@@ -1,1215 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import {
- PipelineEditor,
- PipelineOutOfDateError,
- ThemeProvider,
-} from '@elyra/pipeline-editor';
-import {
- migrate,
- validate,
- ComponentNotFoundError,
-} from '@elyra/pipeline-services';
-import { ContentParser } from '@elyra/services';
-import {
- IconUtil,
- clearPipelineIcon,
- exportPipelineIcon,
- pipelineIcon,
- savePipelineIcon,
- showBrowseFileDialog,
- runtimesIcon,
- containerIcon,
- Dropzone,
- RequestErrors,
- showFormDialog,
- componentCatalogIcon,
-} from '@elyra/ui-components';
-import { ILabShell } from '@jupyterlab/application';
-import { Dialog, ReactWidget, showDialog } from '@jupyterlab/apputils';
-import { PathExt } from '@jupyterlab/coreutils';
-import {
- DocumentRegistry,
- ABCWidgetFactory,
- DocumentWidget,
- Context,
-} from '@jupyterlab/docregistry';
-import { IFileBrowserFactory } from '@jupyterlab/filebrowser';
-import { ISettingRegistry } from '@jupyterlab/settingregistry';
-
-import 'carbon-components/css/carbon-components.min.css';
-
-import { toArray } from '@lumino/algorithm';
-import { IDragEvent } from '@lumino/dragdrop';
-import { Signal } from '@lumino/signaling';
-
-import React, { useCallback, useEffect, useRef, useState } from 'react';
-import { ToastContainer, toast } from 'react-toastify';
-import 'react-toastify/dist/ReactToastify.css';
-
-import {
- EmptyGenericPipeline,
- EmptyPlatformSpecificPipeline,
-} from './EmptyPipelineContent';
-import { formDialogWidget } from './formDialogWidget';
-import {
- usePalette,
- useRuntimeImages,
- useRuntimesSchema,
-} from './pipeline-hooks';
-import { PipelineExportDialog } from './PipelineExportDialog';
-import {
- PipelineService,
- RUNTIMES_SCHEMASPACE,
- RUNTIME_IMAGES_SCHEMASPACE,
- COMPONENT_CATALOGS_SCHEMASPACE,
-} from './PipelineService';
-import { PipelineSubmissionDialog } from './PipelineSubmissionDialog';
-import {
- createRuntimeData,
- getConfigDetails,
- IRuntimeData,
-} from './runtime-utils';
-import { theme } from './theme';
-
-const PIPELINE_CLASS = 'elyra-PipelineEditor';
-
-export const commandIDs = {
- openPipelineEditor: 'pipeline-editor:open',
- openMetadata: 'elyra-metadata:open',
- openDocManager: 'docmanager:open',
- newDocManager: 'docmanager:new-untitled',
- saveDocManager: 'docmanager:save',
- submitScript: 'script-editor:submit',
- submitNotebook: 'notebook:submit',
- addFileToPipeline: 'pipeline-editor:add-node',
- refreshPalette: 'pipeline-editor:refresh-palette',
- openViewer: 'elyra-code-viewer:open',
-};
-
-const getAllPaletteNodes = (palette: any): any[] => {
- if (palette.categories === undefined) {
- return [];
- }
-
- const nodes = [];
- for (const c of palette.categories) {
- if (c.node_types) {
- nodes.push(...c.node_types);
- }
- }
-
- return nodes;
-};
-
-const isRuntimeTypeAvailable = (data: IRuntimeData, type?: string): boolean => {
- for (const p of data.platforms) {
- if (type === undefined || p.id === type) {
- if (p.configs.length > 0) {
- return true;
- }
- }
- }
- return false;
-};
-
-const getDisplayName = (
- runtimesSchema: any,
- type?: string,
-): string | undefined => {
- if (!type) {
- return undefined;
- }
- const schema = runtimesSchema?.find((s: any) => s.runtime_type === type);
- return schema?.title;
-};
-
-class PipelineEditorWidget extends ReactWidget {
- browserFactory: IFileBrowserFactory;
- shell: ILabShell;
- commands: any;
- addFileToPipelineSignal: Signal;
- refreshPaletteSignal: Signal;
- context: Context;
- settings: ISettingRegistry.ISettings;
-
- constructor(options: any) {
- super(options);
- this.browserFactory = options.browserFactory;
- this.shell = options.shell;
- this.commands = options.commands;
- this.addFileToPipelineSignal = options.addFileToPipelineSignal;
- this.refreshPaletteSignal = options.refreshPaletteSignal;
- this.context = options.context;
- this.settings = options.settings;
- let nullPipeline = this.context.model.toJSON() === null;
- this.context.model.contentChanged.connect(() => {
- if (nullPipeline) {
- nullPipeline = false;
- this.update();
- }
- });
- }
-
- render(): any {
- if (this.context.model.toJSON() === null) {
- return
;
- }
- return (
-
- );
- }
-}
-
-interface IProps {
- context: DocumentRegistry.Context;
- browserFactory: IFileBrowserFactory;
- shell: ILabShell;
- commands: any;
- addFileToPipelineSignal: Signal;
- refreshPaletteSignal: Signal;
- settings?: ISettingRegistry.ISettings;
- widgetId?: string;
-}
-
-const PipelineWrapper: React.FC = ({
- context,
- browserFactory,
- shell,
- commands,
- addFileToPipelineSignal,
- refreshPaletteSignal,
- settings,
- widgetId,
-}) => {
- const ref = useRef(null);
- const [loading, setLoading] = useState(true);
- const [pipeline, setPipeline] = useState(context.model.toJSON());
- const [panelOpen, setPanelOpen] = React.useState(false);
-
- const type: string | undefined =
- pipeline?.pipelines?.[0]?.app_data?.runtime_type;
-
- const { data: runtimesSchema, error: runtimesSchemaError } =
- useRuntimesSchema();
-
- const doubleClickToOpenProperties =
- settings?.composite['doubleClickToOpenProperties'] ?? true;
-
- const runtimeDisplayName = getDisplayName(runtimesSchema, type) ?? 'Generic';
-
- const {
- data: palette,
- error: paletteError,
- mutate: mutatePalette,
- } = usePalette(type);
-
- useEffect(() => {
- const handleMutateSignal = (): void => {
- mutatePalette();
- };
- refreshPaletteSignal.connect(handleMutateSignal);
- return (): void => {
- refreshPaletteSignal.disconnect(handleMutateSignal);
- };
- }, [refreshPaletteSignal, mutatePalette]);
-
- const { data: runtimeImages, error: runtimeImagesError } = useRuntimeImages();
-
- useEffect(() => {
- if (runtimeImages?.length === 0) {
- RequestErrors.noMetadataError('runtime image');
- }
- }, [runtimeImages?.length]);
-
- useEffect(() => {
- if (paletteError) {
- RequestErrors.serverError(paletteError);
- shell.currentWidget?.close();
- }
- }, [paletteError, shell.currentWidget]);
-
- useEffect(() => {
- if (runtimeImagesError) {
- RequestErrors.serverError(runtimeImagesError);
- shell.currentWidget?.close();
- }
- }, [runtimeImagesError, shell.currentWidget]);
-
- useEffect(() => {
- if (runtimesSchemaError) {
- RequestErrors.serverError(runtimesSchemaError);
- shell.currentWidget?.close();
- }
- }, [runtimesSchemaError, shell.currentWidget]);
-
- const contextRef = useRef(context);
- useEffect(() => {
- const currentContext = contextRef.current;
-
- const changeHandler = (): void => {
- const pipelineJson: any = currentContext.model.toJSON();
-
- // map IDs to display names
- const nodes = pipelineJson?.pipelines?.[0]?.nodes;
- if (nodes?.length > 0) {
- for (const node of nodes) {
- if (node?.app_data?.component_parameters) {
- for (const [key, val] of Object.entries(
- node?.app_data?.component_parameters,
- )) {
- if (val === null) {
- node.app_data.component_parameters[key] = undefined;
- }
- }
- }
- }
- }
- // TODO: don't persist this, but this will break things right now
- if (pipelineJson?.pipelines?.[0]?.app_data) {
- if (!pipelineJson.pipelines[0].app_data.properties) {
- pipelineJson.pipelines[0].app_data.properties = {};
- }
- const pipeline_path = contextRef.current.path;
- const pipeline_name = PathExt.basename(
- pipeline_path,
- PathExt.extname(pipeline_path),
- );
- pipelineJson.pipelines[0].app_data.properties.name = pipeline_name;
- pipelineJson.pipelines[0].app_data.properties.runtime =
- runtimeDisplayName;
- }
- setPipeline(pipelineJson);
- setLoading(false);
- };
-
- currentContext.ready.then(changeHandler);
- currentContext.model.contentChanged.connect(changeHandler);
-
- return (): void => {
- currentContext.model.contentChanged.disconnect(changeHandler);
- };
- }, [runtimeDisplayName]);
-
- const onChange = useCallback((pipelineJson: any): void => {
- const removeNullValues = (data: any, removeEmptyString?: boolean): void => {
- for (const key in data) {
- if (
- data[key] === null ||
- data[key] === undefined ||
- (removeEmptyString && data[key] === '')
- ) {
- delete data[key];
- } else if (Array.isArray(data[key])) {
- const newArray = [];
- for (const i in data[key]) {
- if (typeof data[key][i] === 'object') {
- removeNullValues(data[key][i], true);
- if (Object.keys(data[key][i]).length > 0) {
- newArray.push(data[key][i]);
- }
- } else if (data[key][i] !== null && data[key][i] !== '') {
- newArray.push(data[key][i]);
- }
- }
- data[key] = newArray;
- } else if (typeof data[key] === 'object') {
- removeNullValues(data[key]);
- }
- }
- };
-
- // Remove all null values from the pipeline
- for (const node of pipelineJson?.pipelines?.[0]?.nodes ?? []) {
- removeNullValues(node.app_data ?? {});
- }
- removeNullValues(
- pipelineJson?.pipelines?.[0]?.app_data?.properties?.pipeline_defaults ??
- {},
- );
- if (contextRef.current.isReady) {
- contextRef.current.model.fromString(
- JSON.stringify(pipelineJson, null, 2),
- );
- }
- }, []);
-
- const isDialogAlreadyShowing = useRef(false);
- const onError = useCallback(
- (error?: Error): void => {
- if (isDialogAlreadyShowing.current) {
- return; // bail, we are already showing a dialog.
- }
- isDialogAlreadyShowing.current = true;
- if (error instanceof PipelineOutOfDateError) {
- showDialog({
- title: 'Migrate pipeline?',
- body: (
-
- This pipeline corresponds to an older version of Elyra and needs
- to be migrated.
-
- Although the pipeline can be further edited and/or submitted after
- its update,
-
- the migration will not be completed until the pipeline has been
- saved within the editor.
-
-
- Proceed with migration?
-
- ),
- buttons: [Dialog.cancelButton(), Dialog.okButton()],
- }).then(async (result) => {
- isDialogAlreadyShowing.current = false;
- if (result.button.accept) {
- // proceed with migration
- console.log('migrating pipeline');
- const pipelineJSON: any = contextRef.current.model.toJSON();
- try {
- const migratedPipeline = migrate(pipelineJSON, (pipeline) => {
- // function for updating to relative paths in v2
- // uses location of filename as expected in v1
- for (const node of pipeline.nodes) {
- node.app_data.filename =
- PipelineService.getPipelineRelativeNodePath(
- contextRef.current.path,
- node.app_data.filename,
- );
- }
- return pipeline;
- });
- contextRef.current.model.fromString(
- JSON.stringify(migratedPipeline, null, 2),
- );
- } catch (migrationError) {
- if (migrationError instanceof ComponentNotFoundError) {
- showDialog({
- title: 'Pipeline migration aborted!',
- body: (
-
- {' '}
- The pipeline you are trying to migrate uses example
- components, which are not
- enabled in your environment. Complete the setup
- instructions in{' '}
-
- Example Custom Components
- {' '}
- and try again.
-
- ),
- buttons: [Dialog.okButton({ label: 'Close' })],
- }).then(() => {
- shell.currentWidget?.close();
- });
- } else {
- showDialog({
- title: 'Pipeline migration failed!',
- body: {migrationError?.message || ''}
,
- buttons: [Dialog.okButton()],
- }).then(() => {
- shell.currentWidget?.close();
- });
- }
- }
- } else {
- shell.currentWidget?.close();
- }
- });
- } else {
- showDialog({
- title: 'Load pipeline failed!',
- body: {error?.message || ''}
,
- buttons: [Dialog.okButton()],
- }).then(() => {
- isDialogAlreadyShowing.current = false;
- shell.currentWidget?.close();
- });
- }
- },
- [shell.currentWidget],
- );
-
- const onFileRequested = async (args: any): Promise => {
- const filename = PipelineService.getWorkspaceRelativeNodePath(
- contextRef.current.path,
- args.filename ?? '',
- );
- if (args.propertyID.includes('dependencies')) {
- const res = await showBrowseFileDialog(
- browserFactory.defaultBrowser.model.manager,
- {
- multiselect: true,
- includeDir: true,
- rootPath: PathExt.dirname(filename),
- filter: (model: any): boolean => {
- return model.path !== filename;
- },
- },
- );
-
- if (res.button.accept && res.value.length) {
- return res.value.map((v: any) => v.path);
- }
- } else {
- const res = await showBrowseFileDialog(
- browserFactory.defaultBrowser.model.manager,
- {
- startPath: PathExt.dirname(filename),
- filter: (model: any): boolean => {
- if (args.filters?.File === undefined) {
- return true;
- }
-
- const ext = PathExt.extname(model.path);
- return args.filters.File.includes(ext);
- },
- },
- );
-
- if (res.button.accept && res.value.length) {
- const file = PipelineService.getPipelineRelativeNodePath(
- contextRef.current.path,
- res.value[0].path,
- );
- return [file];
- }
- }
-
- return undefined;
- };
-
- const onPropertiesUpdateRequested = async (args: any): Promise => {
- if (!contextRef.current.path) {
- return args;
- }
- const path = PipelineService.getWorkspaceRelativeNodePath(
- contextRef.current.path,
- args.component_parameters.filename,
- );
- const new_env_vars = await ContentParser.getEnvVars(path).then(
- (response: any) =>
- response.map((str: string) => {
- return { env_var: str };
- }),
- );
-
- const env_vars = args.component_parameters?.env_vars ?? [];
- const merged_env_vars = [
- ...env_vars,
- ...new_env_vars.filter(
- (new_var: any) =>
- !env_vars.some((old_var: any) => {
- return old_var.env_var === new_var.env_var;
- }),
- ),
- ];
-
- return {
- ...args,
- component_parameters: {
- ...args.component_parameters,
- env_vars: merged_env_vars.filter(Boolean),
- },
- };
- };
-
- const handleOpenComponentDef = useCallback(
- (componentId: string, componentSource: string) => {
- // Show error dialog if the component does not exist
- if (!componentId) {
- const dialogBody = [];
- try {
- const componentSourceJson = JSON.parse(componentSource);
- dialogBody.push(`catalog_type: ${componentSourceJson.catalog_type}`);
- for (const [key, value] of Object.entries(
- componentSourceJson.component_ref,
- )) {
- dialogBody.push(`${key}: ${value}`);
- }
- } catch {
- dialogBody.push(componentSource);
- }
- return showDialog({
- title: 'Component not found',
- body: (
-
- This node uses a component that is not stored in your component
- registry.
- {dialogBody.map((line, i) => (
-
-
- {line}
-
- ))}
-
-
-
- Learn more...
-
-
- ),
- buttons: [Dialog.okButton()],
- });
- }
- return PipelineService.getComponentDef(type, componentId)
- .then((res) => {
- const nodeDef = getAllPaletteNodes(palette).find(
- (n) => n.id === componentId,
- );
- commands.execute(commandIDs.openViewer, {
- content: res.content,
- mimeType: res.mimeType,
- label: nodeDef?.label ?? componentId,
- });
- })
- .catch((e) => RequestErrors.serverError(e));
- },
- [commands, palette, type],
- );
-
- const onDoubleClick = (data: any): void => {
- for (let i = 0; i < data.selectedObjectIds.length; i++) {
- const node = pipeline.pipelines[0].nodes.find(
- (node: any) => node.id === data.selectedObjectIds[i],
- );
- const nodeDef = getAllPaletteNodes(palette).find(
- (n) => n.op === node?.op,
- );
- if (node?.app_data?.component_parameters?.filename) {
- commands.execute(commandIDs.openDocManager, {
- path: PipelineService.getWorkspaceRelativeNodePath(
- contextRef.current.path,
- node.app_data.component_parameters.filename,
- ),
- });
- } else if (!nodeDef?.app_data?.parameter_refs?.['filehandler']) {
- handleOpenComponentDef(nodeDef?.id, node?.app_data?.component_source);
- }
- }
- };
-
- const handleSubmission = useCallback(
- async (actionType: 'run' | 'export'): Promise => {
- const pipelineJson: any = context.model.toJSON();
- // Check that all nodes are valid
- const errorMessages = validate(
- JSON.stringify(pipelineJson),
- getAllPaletteNodes(palette),
- palette.properties,
- );
- if (errorMessages && errorMessages.length > 0) {
- let errorMessage = '';
- for (const error of errorMessages) {
- errorMessage += (errorMessage ? '\n' : '') + error.message;
- }
- toast.error(`Failed ${actionType}: ${errorMessage}`);
- return;
- }
-
- if (contextRef.current.model.dirty) {
- const dialogResult = await showDialog({
- title:
- 'This pipeline contains unsaved changes. To submit the pipeline the changes need to be saved.',
- buttons: [
- Dialog.cancelButton(),
- Dialog.okButton({ label: 'Save and Submit' }),
- ],
- });
- if (dialogResult.button && dialogResult.button.accept === true) {
- await contextRef.current.save();
- } else {
- // Don't proceed if cancel button pressed
- return;
- }
- }
-
- const pipelineName = PathExt.basename(
- contextRef.current.path,
- PathExt.extname(contextRef.current.path),
- );
-
- // TODO: Parallelize this
- const runtimeTypes = await PipelineService.getRuntimeTypes();
- const runtimes = await PipelineService.getRuntimes()
- .then((runtimeList) => {
- return runtimeList.filter((runtime: any) => {
- return (
- !runtime.metadata.runtime_enabled &&
- !!runtimeTypes.find(
- (r: any) => runtime.metadata.runtime_type === r.id,
- )
- );
- });
- })
- .catch((error) => RequestErrors.serverError(error));
- const schema = await PipelineService.getRuntimesSchema().catch((error) =>
- RequestErrors.serverError(error),
- );
-
- const runtimeData = createRuntimeData({
- schema,
- runtimes,
- allowLocal: actionType === 'run',
- });
-
- let title =
- type !== undefined
- ? `${actionType} pipeline for ${runtimeDisplayName}`
- : `${actionType} pipeline`;
-
- if (actionType === 'export' || type !== undefined) {
- if (!isRuntimeTypeAvailable(runtimeData, type)) {
- const res = await RequestErrors.noMetadataError(
- 'runtime',
- `${actionType} pipeline.`,
- type !== undefined ? runtimeDisplayName : undefined,
- );
-
- if (res.button.label.includes(RUNTIMES_SCHEMASPACE)) {
- // Open the runtimes widget
- shell.activateById(`elyra-metadata:${RUNTIMES_SCHEMASPACE}`);
- }
- return;
- }
- }
- // Capitalize
- title = title.charAt(0).toUpperCase() + title.slice(1);
-
- let dialogOptions: Partial>;
-
- pipelineJson.pipelines[0].app_data.properties.pipeline_parameters =
- pipelineJson.pipelines[0].app_data.properties.pipeline_parameters?.filter(
- (param: any) => {
- return !!pipelineJson.pipelines[0].nodes.find((node: any) => {
- return (
- param.name !== '' &&
- (node.app_data.component_parameters?.pipeline_parameters?.includes(
- param.name,
- ) ||
- Object.values(node.app_data.component_parameters ?? {}).find(
- (property: any) =>
- property.widget === 'parameter' &&
- property.value === param.name,
- ))
- );
- });
- },
- );
-
- const parameters =
- pipelineJson?.pipelines[0].app_data.properties.pipeline_parameters;
-
- switch (actionType) {
- case 'run':
- dialogOptions = {
- title,
- body: formDialogWidget(
- ,
- ),
- buttons: [Dialog.cancelButton(), Dialog.okButton()],
- defaultButton: 1,
- focusNodeSelector: '#pipeline_name',
- };
- break;
- case 'export':
- dialogOptions = {
- title,
- body: formDialogWidget(
- ,
- ),
- buttons: [Dialog.cancelButton(), Dialog.okButton()],
- defaultButton: 1,
- focusNodeSelector: '#runtime_config',
- };
- break;
- }
-
- const dialogResult = await showFormDialog(dialogOptions);
-
- if (dialogResult.value === null) {
- // When Cancel is clicked on the dialog, just return
- return;
- }
-
- // Clean null properties
- for (const node of pipelineJson.pipelines[0].nodes) {
- if (node.app_data.component_parameters.cpu === null) {
- delete node.app_data.component_parameters.cpu;
- }
- if (node.app_data.component_parameters.memory === null) {
- delete node.app_data.component_parameters.memory;
- }
- if (node.app_data.component_parameters.gpu === null) {
- delete node.app_data.component_parameters.gpu;
- }
- }
-
- const configDetails = getConfigDetails(
- runtimeData,
- dialogResult.value.runtime_config,
- );
-
- PipelineService.setNodePathsRelativeToWorkspace(
- pipelineJson.pipelines[0],
- getAllPaletteNodes(palette),
- contextRef.current.path,
- );
-
- // Metadata
- pipelineJson.pipelines[0].app_data.name =
- dialogResult.value.pipeline_name ?? pipelineName;
- pipelineJson.pipelines[0].app_data.source = PathExt.basename(
- contextRef.current.path,
- );
-
- // Pipeline parameter overrides
- for (const paramIndex in parameters ?? []) {
- const param = parameters[paramIndex];
- if (param.name) {
- let paramOverride = dialogResult.value[`${param.name}-paramInput`];
- if (
- (param.default_value?.type === 'Integer' ||
- param.default_value?.type === 'Float') &&
- paramOverride !== ''
- ) {
- paramOverride = Number(paramOverride);
- }
- pipelineJson.pipelines[0].app_data.properties.pipeline_parameters[
- paramIndex
- ].value =
- paramOverride === '' ? param.default_value?.value : paramOverride;
- }
- }
-
- // Pipeline name
- pipelineJson.pipelines[0].app_data.name =
- dialogResult.value.pipeline_name ?? pipelineName;
-
- // Runtime info
- pipelineJson.pipelines[0].app_data.runtime_config =
- configDetails?.id ?? null;
-
- // Export info
- const pipeline_dir = PathExt.dirname(contextRef.current.path);
- const basePath = pipeline_dir ? `${pipeline_dir}/` : '';
- const exportType = dialogResult.value.pipeline_filetype;
- const exportName = dialogResult.value.export_name;
- const exportPath = `${basePath}${exportName}.${exportType}`;
-
- switch (actionType) {
- case 'run':
- PipelineService.submitPipeline(
- pipelineJson,
- configDetails?.platform.displayName ?? '',
- ).catch((error) => RequestErrors.serverError(error));
- break;
- case 'export':
- PipelineService.exportPipeline(
- pipelineJson,
- exportType,
- exportPath,
- dialogResult.value.overwrite,
- ).catch((error) => RequestErrors.serverError(error));
- break;
- }
- },
- [context.model, palette, runtimeDisplayName, type, shell],
- );
-
- const handleClearPipeline = useCallback(async (data: any): Promise => {
- return showDialog({
- title: 'Clear Pipeline',
- body: 'Are you sure you want to clear the pipeline?',
- buttons: [
- Dialog.cancelButton(),
- Dialog.okButton({ label: 'Clear All' }),
- Dialog.okButton({ label: 'Clear Canvas' }),
- ],
- }).then((result) => {
- if (result.button.accept) {
- const newPipeline: any = contextRef.current.model.toJSON();
- if (newPipeline?.pipelines?.[0]?.nodes?.length > 0) {
- newPipeline.pipelines[0].nodes = [];
- }
- // remove supernode pipelines
- newPipeline.pipelines = [newPipeline.pipelines[0]];
- // only clear pipeline properties when "Clear All" is selected
- if (result.button.label === 'Clear All') {
- const pipelineProperties =
- newPipeline?.pipelines?.[0]?.app_data?.properties;
- if (pipelineProperties) {
- // Remove all fields of pipeline properties except for the name/runtime (readonly)
- newPipeline.pipelines[0].app_data.properties = {
- name: pipelineProperties.name,
- runtime: pipelineProperties.runtime,
- };
- }
- }
- contextRef.current.model.fromJSON(newPipeline);
- }
- });
- }, []);
-
- const onAction = useCallback(
- (args: { type: string; payload?: any }) => {
- switch (args.type) {
- case 'save':
- contextRef.current.save();
- break;
- case 'run':
- case 'export':
- handleSubmission(args.type);
- break;
- case 'clear':
- handleClearPipeline(args.payload);
- break;
- case 'toggleOpenPanel':
- setPanelOpen(!panelOpen);
- break;
- case 'properties':
- setPanelOpen(true);
- break;
- case 'openRuntimes':
- shell.activateById(`elyra-metadata:${RUNTIMES_SCHEMASPACE}`);
- break;
- case 'openRuntimeImages':
- shell.activateById(`elyra-metadata:${RUNTIME_IMAGES_SCHEMASPACE}`);
- break;
- case 'openComponentCatalogs':
- shell.activateById(
- `elyra-metadata:${COMPONENT_CATALOGS_SCHEMASPACE}`,
- );
- break;
- case 'openFile':
- commands.execute(commandIDs.openDocManager, {
- path: PipelineService.getWorkspaceRelativeNodePath(
- contextRef.current.path,
- args.payload,
- ),
- });
- break;
- case 'openComponentDef':
- handleOpenComponentDef(
- args.payload.componentId,
- args.payload.componentSource,
- );
- break;
- default:
- break;
- }
- },
- [
- handleSubmission,
- handleClearPipeline,
- panelOpen,
- shell,
- commands,
- handleOpenComponentDef,
- ],
- );
-
- const toolbar = {
- leftBar: [
- {
- action: 'run',
- label: 'Run Pipeline',
- enable: true,
- },
- {
- action: 'save',
- label: 'Save Pipeline',
- enable: true,
- iconEnabled: IconUtil.encode(savePipelineIcon),
- iconDisabled: IconUtil.encode(savePipelineIcon),
- },
- {
- action: 'export',
- label: 'Export Pipeline',
- enable: true,
- iconEnabled: IconUtil.encode(exportPipelineIcon),
- iconDisabled: IconUtil.encode(exportPipelineIcon),
- },
- {
- action: 'clear',
- label: 'Clear Pipeline',
- enable: true,
- iconEnabled: IconUtil.encode(clearPipelineIcon),
- iconDisabled: IconUtil.encode(clearPipelineIcon),
- },
- {
- action: 'openRuntimes',
- label: 'Open Runtimes',
- enable: true,
- iconEnabled: IconUtil.encode(runtimesIcon),
- iconDisabled: IconUtil.encode(runtimesIcon),
- },
- {
- action: 'openRuntimeImages',
- label: 'Open Runtime Images',
- enable: true,
- iconEnabled: IconUtil.encode(containerIcon),
- iconDisabled: IconUtil.encode(containerIcon),
- },
- {
- action: 'openComponentCatalogs',
- label: 'Open Component Catalogs',
- enable: true,
- iconEnabled: IconUtil.encode(componentCatalogIcon),
- iconDisabled: IconUtil.encode(componentCatalogIcon),
- },
- { action: 'undo', label: 'Undo' },
- { action: 'redo', label: 'Redo' },
- { action: 'cut', label: 'Cut' },
- { action: 'copy', label: 'Copy' },
- { action: 'paste', label: 'Paste' },
- { action: 'createAutoComment', label: 'Add Comment', enable: true },
- { action: 'deleteSelectedObjects', label: 'Delete' },
- {
- action: 'arrangeHorizontally',
- label: 'Arrange Horizontally',
- enable: true,
- },
- {
- action: 'arrangeVertically',
- label: 'Arrange Vertically',
- enable: true,
- },
- ],
- rightBar: [
- {
- action: '',
- label: `Runtime: ${runtimeDisplayName}`,
- incLabelWithIcon: 'before',
- enable: false,
- kind: 'tertiary',
- // TODO: re-add icon
- // iconEnabled: IconUtil.encode(ICON_MAP[type ?? ''] ?? pipelineIcon)
- },
- {
- action: 'toggleOpenPanel',
- label: panelOpen ? 'Close Panel' : 'Open Panel',
- enable: true,
- iconTypeOverride: panelOpen ? 'paletteOpen' : 'paletteClose',
- },
- ],
- };
-
- const [defaultPosition, setDefaultPosition] = useState(10);
-
- const handleAddFileToPipeline = useCallback(
- (location?: { x: number; y: number }) => {
- const fileBrowser = browserFactory.defaultBrowser;
- // Only add file to pipeline if it is currently in focus
- if (shell.currentWidget?.id !== widgetId) {
- return;
- }
-
- let failedAdd = 0;
- let position = 0;
- const missingXY = !location;
-
- // if either x or y is undefined use the default coordinates
- if (missingXY) {
- position = defaultPosition;
- location = {
- x: 75,
- y: 85,
- };
- }
-
- toArray(fileBrowser.selectedItems()).map((item: any): void => {
- if (PipelineService.isSupportedNode(item)) {
- item.op = PipelineService.getNodeType(item.path);
- item.path = PipelineService.getPipelineRelativeNodePath(
- contextRef.current.path,
- item.path,
- );
- item.x = (location?.x ?? 0) + position;
- item.y = (location?.y ?? 0) + position;
-
- const success = ref.current?.addFile({
- nodeTemplate: {
- op: item.op,
- },
- offsetX: item.x,
- offsetY: item.y,
- path: item.path,
- });
-
- if (success) {
- position += 20;
- } else {
- // handle error
- }
- } else {
- failedAdd++;
- }
- });
- // update position if the default coordinates were used
- if (missingXY) {
- setDefaultPosition(position);
- }
-
- if (failedAdd) {
- return showDialog({
- title: 'Unsupported File(s)',
- body: 'Only supported files (Notebooks, Python scripts, and R scripts) can be added to a pipeline.',
- buttons: [Dialog.okButton()],
- });
- }
-
- return;
- },
- [browserFactory.defaultBrowser, defaultPosition, shell, widgetId],
- );
-
- const handleDrop = async (e: IDragEvent): Promise => {
- handleAddFileToPipeline({ x: e.offsetX, y: e.offsetY });
- };
-
- useEffect(() => {
- const handleSignal = (): void => {
- handleAddFileToPipeline();
- };
- addFileToPipelineSignal.connect(handleSignal);
- return (): void => {
- addFileToPipelineSignal.disconnect(handleSignal);
- };
- }, [addFileToPipelineSignal, handleAddFileToPipeline]);
-
- if (loading || palette === undefined) {
- return
;
- }
-
- const handleOpenCatalog = (): void => {
- shell.activateById(`elyra-metadata:${COMPONENT_CATALOGS_SCHEMASPACE}`);
- };
-
- const handleOpenSettings = (): void => {
- commands.execute('settingeditor:open', { query: 'Pipeline Editor' });
- };
-
- return (
-
-
-
-
- {type === undefined ? (
-
- ) : (
-
- )}
-
-
-
- );
-};
-
-export class PipelineEditorFactory extends ABCWidgetFactory {
- browserFactory: IFileBrowserFactory;
- shell: ILabShell;
- commands: any;
- addFileToPipelineSignal: Signal;
- refreshPaletteSignal: Signal;
- settings: ISettingRegistry.ISettings;
-
- constructor(options: any) {
- super(options);
- this.browserFactory = options.browserFactory;
- this.shell = options.shell;
- this.commands = options.commands;
- this.addFileToPipelineSignal = new Signal(this);
- this.refreshPaletteSignal = new Signal(this);
- this.settings = options.settings;
- }
-
- protected createNewWidget(context: DocumentRegistry.Context): DocumentWidget {
- // Creates a blank widget with a DocumentWidget wrapper
- const props = {
- shell: this.shell,
- commands: this.commands,
- browserFactory: this.browserFactory,
- context: context,
- addFileToPipelineSignal: this.addFileToPipelineSignal,
- refreshPaletteSignal: this.refreshPaletteSignal,
- settings: this.settings,
- };
- const content = new PipelineEditorWidget(props);
-
- const widget = new DocumentWidget({ content, context });
- widget.addClass(PIPELINE_CLASS);
- widget.title.icon = pipelineIcon;
- return widget;
- }
-}
diff --git a/packages/pipeline-editor/schema/src/PipelineExportDialog.tsx b/packages/pipeline-editor/schema/src/PipelineExportDialog.tsx
deleted file mode 100644
index ca5ccddf4..000000000
--- a/packages/pipeline-editor/schema/src/PipelineExportDialog.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import * as React from 'react';
-
-import { IParameterProps, ParameterInputForm } from './ParameterInputForm';
-
-import { IRuntimeType } from './PipelineService';
-import { IRuntimeData } from './runtime-utils';
-import RuntimeConfigSelect from './RuntimeConfigSelect';
-
-interface IFileTypeSelectProps {
- fileTypes: { display_name: string; id: string }[];
-}
-
-const FileTypeSelect: React.FC = ({ fileTypes }) => {
- return (
- <>
- Export Pipeline as:
-
-
- {fileTypes.map((f) => (
-
- {f.display_name}
-
- ))}
-
- >
- );
-};
-
-interface IProps extends IParameterProps {
- runtimeData: IRuntimeData;
- runtimeTypeInfo: IRuntimeType[];
- pipelineType?: string;
- exportName: string;
-}
-
-export const PipelineExportDialog: React.FC = ({
- runtimeData,
- runtimeTypeInfo,
- pipelineType,
- exportName,
- parameters,
-}) => {
- return (
-
- );
-};
diff --git a/packages/pipeline-editor/schema/src/PipelineService.tsx b/packages/pipeline-editor/schema/src/PipelineService.tsx
deleted file mode 100644
index be220e548..000000000
--- a/packages/pipeline-editor/schema/src/PipelineService.tsx
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { MetadataService, IDictionary, RequestHandler } from '@elyra/services';
-import { RequestErrors } from '@elyra/ui-components';
-
-import { showDialog, Dialog } from '@jupyterlab/apputils';
-import { PathExt } from '@jupyterlab/coreutils';
-
-import * as React from 'react';
-
-export const KFP_SCHEMA = 'kfp';
-export const RUNTIMES_SCHEMASPACE = 'runtimes';
-export const RUNTIME_IMAGES_SCHEMASPACE = 'runtime-images';
-export const COMPONENT_CATALOGS_SCHEMASPACE = 'component-catalogs';
-
-export interface IRuntime {
- name: string;
- display_name: string;
- schema_name: string;
- metadata: {
- runtime_type: string;
- };
-}
-
-export interface ISchema {
- name: string;
- title: string;
- runtime_type: string;
-}
-
-interface IComponentDef {
- content: string;
- mimeType: string;
-}
-
-enum ContentType {
- notebook = 'execute-notebook-node',
- python = 'execute-python-node',
- r = 'execute-r-node',
- other = 'other',
-}
-
-const CONTENT_TYPE_MAPPER: Map = new Map([
- ['.py', ContentType.python],
- ['.ipynb', ContentType.notebook],
- ['.r', ContentType.r],
-]);
-
-export interface IRuntimeType {
- runtime_enabled: boolean;
- id: string;
- display_name: string;
- icon: string;
- export_file_types: { id: string; display_name: string }[];
-}
-
-export class PipelineService {
- /**
- * Returns a list of resources corresponding to each active runtime-type.
- */
- static async getRuntimeTypes(): Promise {
- const res = await RequestHandler.makeGetRequest(
- 'elyra/pipeline/runtimes/types',
- );
- return res.runtime_types.sort((a: any, b: any) => a.id.localeCompare(b.id));
- }
-
- /**
- * Returns a list of external runtime configurations available as
- * `runtimes metadata`. This is used to submit the pipeline to be
- * executed on these runtimes.
- */
- static async getRuntimes(): Promise {
- return MetadataService.getMetadata(RUNTIMES_SCHEMASPACE);
- }
-
- /**
- * Returns a list of runtime schema
- */
- static async getRuntimesSchema(showError = true): Promise {
- return MetadataService.getSchema(RUNTIMES_SCHEMASPACE).then((schema) => {
- if (showError && Object.keys(schema).length === 0) {
- return RequestErrors.noMetadataError('schema');
- }
-
- return schema;
- });
- }
-
- /**
- * Return a list of configured container images that are used as runtimes environments
- * to run the pipeline nodes.
- */
- static async getRuntimeImages(): Promise {
- try {
- let runtimeImages = await MetadataService.getMetadata('runtime-images');
-
- runtimeImages = runtimeImages.sort(
- (a: any, b: any) => 0 - (a.name > b.name ? -1 : 1),
- );
-
- if (Object.keys(runtimeImages).length === 0) {
- return RequestErrors.noMetadataError('runtime image');
- }
-
- const images: IDictionary = {};
- for (const image in runtimeImages) {
- const imageName: string =
- runtimeImages[image]['metadata']['image_name'];
- images[imageName] = runtimeImages[image]['display_name'];
- }
- return images;
- } catch (error) {
- Promise.reject(error);
- }
- }
-
- static async getComponentDef(
- type = 'local',
- componentID: string,
- ): Promise {
- return await RequestHandler.makeGetRequest(
- `elyra/pipeline/components/${type}/${componentID}`,
- );
- }
-
- /**
- * Submit a request to refresh the component cache. If catalogName is given
- * only refreshes the given catalog
- *
- * @param catalogName
- */
- static async refreshComponentsCache(catalogName?: string): Promise {
- return await RequestHandler.makePutRequest(
- `elyra/pipeline/components/cache${catalogName ? '/' + catalogName : ''}`,
- JSON.stringify({ action: 'refresh' }),
- );
- }
-
- /**
- * Creates a Dialog for passing to makeServerRequest
- */
- static getWaitDialog(
- title = 'Making server request...',
- body = 'This may take some time',
- ): Dialog {
- return new Dialog({
- title: title,
- body: body,
- buttons: [Dialog.okButton()],
- });
- }
-
- /**
- * Submit the pipeline to be executed on an external runtime (e.g. Kbeflow Pipelines)
- *
- * @param pipeline
- * @param runtimeName
- */
- static async submitPipeline(
- pipeline: any,
- runtimeName: string,
- ): Promise {
- return RequestHandler.makePostRequest(
- 'elyra/pipeline/schedule',
- JSON.stringify(pipeline),
- this.getWaitDialog('Packaging and submitting pipeline ...'),
- ).then((response) => {
- let dialogTitle;
- let dialogBody;
- if (response['run_url']) {
- // pipeline executed remotely in a runtime of choice
- dialogTitle = 'Job submission to ' + runtimeName + ' succeeded';
- dialogBody = (
-
- {response['platform'] === 'APACHE_AIRFLOW' ? (
-
- Apache Airflow DAG has been pushed to the{' '}
-
- Git repository.
-
-
-
- ) : null}
- Check the status of your job at{' '}
-
- Run Details.
-
- {response['object_storage_path'] !== null ? (
-
- The results and outputs are in the{' '}
- {response['object_storage_path']} working directory in{' '}
-
- object storage
-
- .
-
- ) : null}
-
-
- );
- } else {
- // pipeline executed in-place locally
- dialogTitle = 'Job execution succeeded';
- dialogBody = (
- Your job has been executed in-place in your local environment.
- );
- }
-
- return showDialog({
- title: dialogTitle,
- body: dialogBody,
- buttons: [Dialog.okButton()],
- });
- });
- }
-
- /**
- * Export a pipeline to different formats (e.g. DSL, YAML, etc). These formats
- * are understood by a given runtime.
- *
- * @param pipeline
- * @param pipeline_export_format
- * @param pipeline_export_path
- * @param overwrite
- */
- static async exportPipeline(
- pipeline: any,
- pipeline_export_format: string,
- pipeline_export_path: string,
- overwrite: boolean,
- ): Promise {
- console.log(
- 'Exporting pipeline to [' + pipeline_export_format + '] format',
- );
-
- console.log('Overwriting existing file: ' + overwrite);
-
- const body = {
- pipeline: pipeline,
- export_format: pipeline_export_format,
- export_path: pipeline_export_path,
- overwrite: overwrite,
- };
-
- return RequestHandler.makePostRequest(
- 'elyra/pipeline/export',
- JSON.stringify(body),
- this.getWaitDialog('Generating pipeline artifacts ...'),
- ).then((response) => {
- return showDialog({
- title: 'Pipeline export succeeded',
- body: Exported file: {response['export_path']}
,
- buttons: [Dialog.okButton()],
- });
- });
- }
-
- static getNodeType(filepath: string): string {
- const extension: string = PathExt.extname(filepath);
- const type: string = CONTENT_TYPE_MAPPER.get(extension)!;
-
- // TODO: throw error when file extension is not supported?
- return type;
- }
-
- /**
- * Check if a given file is allowed to be added to the pipeline
- * @param item
- */
- static isSupportedNode(file: any): boolean {
- if (PipelineService.getNodeType(file.path)) {
- return true;
- } else {
- return false;
- }
- }
-
- static getPipelineRelativeNodePath(
- pipelinePath: string,
- nodePath: string,
- ): string {
- const relativePath: string = PathExt.relative(
- PathExt.dirname(pipelinePath),
- nodePath,
- );
- return relativePath;
- }
-
- static getWorkspaceRelativeNodePath(
- pipelinePath: string,
- nodePath: string,
- ): string {
- // since resolve returns an "absolute" path we need to strip off the leading '/'
- const workspacePath: string = PathExt.resolve(
- PathExt.dirname(pipelinePath),
- nodePath,
- );
- return workspacePath;
- }
-
- static setNodePathsRelativeToWorkspace(
- pipeline: any,
- paletteNodes: any[],
- pipelinePath: string,
- ): any {
- for (const node of pipeline.nodes) {
- const nodeDef = paletteNodes.find((n) => {
- return n.op === node.op;
- });
- const parameters =
- nodeDef.app_data.properties.properties.component_parameters.properties;
- for (const param in parameters) {
- if (parameters[param].uihints?.['ui:widget'] === 'file') {
- node.app_data.component_parameters[param] =
- this.getWorkspaceRelativeNodePath(
- pipelinePath,
- node.app_data.component_parameters[param],
- );
- } else if (
- node.app_data.component_parameters[param]?.widget === 'file'
- ) {
- node.app_data.component_parameters[param].value =
- this.getWorkspaceRelativeNodePath(
- pipelinePath,
- node.app_data.component_parameters[param].value,
- );
- }
- }
- }
- return pipeline;
- }
-}
diff --git a/packages/pipeline-editor/schema/src/PipelineSubmissionDialog.tsx b/packages/pipeline-editor/schema/src/PipelineSubmissionDialog.tsx
deleted file mode 100644
index 8ff85a02e..000000000
--- a/packages/pipeline-editor/schema/src/PipelineSubmissionDialog.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import * as React from 'react';
-
-import { IParameterProps, ParameterInputForm } from './ParameterInputForm';
-
-import { IRuntimeData } from './runtime-utils';
-import RuntimeConfigSelect from './RuntimeConfigSelect';
-
-interface IProps extends IParameterProps {
- name: string;
- runtimeData: IRuntimeData;
- pipelineType?: string;
-}
-
-export const PipelineSubmissionDialog: React.FC = ({
- name,
- runtimeData,
- pipelineType,
- parameters,
-}) => {
- return (
-
- );
-};
diff --git a/packages/pipeline-editor/schema/src/RuntimeConfigSelect.tsx b/packages/pipeline-editor/schema/src/RuntimeConfigSelect.tsx
deleted file mode 100644
index eec55127f..000000000
--- a/packages/pipeline-editor/schema/src/RuntimeConfigSelect.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import * as React from 'react';
-
-import { IRuntimeData } from './runtime-utils';
-
-const RUN_LOCALLY_ID = '__elyra_local__';
-
-interface IProps {
- runtimeData: IRuntimeData;
- pipelineType?: string;
- children?(platform: string): JSX.Element;
-}
-
-const RuntimeConfigSelect: React.FC = ({
- runtimeData: { platforms, allowLocal },
- pipelineType,
- children,
-}) => {
- const filteredPlatforms = platforms.filter((p) => p.configs.length > 0);
- if (allowLocal) {
- filteredPlatforms.unshift({
- id: RUN_LOCALLY_ID,
- displayName: 'Run in-place locally',
- configs: [],
- });
- }
-
- // NOTE: platform is only selectable if pipelineType is undefined
- const [platform, setPlatform] = React.useState(
- pipelineType ?? filteredPlatforms[0]?.id,
- );
-
- const handleChange = (e: React.ChangeEvent): void => {
- setPlatform(e.target.value);
- };
-
- const configs =
- filteredPlatforms.find((p) => p.id === platform)?.configs ?? [];
- configs.sort((a, b) => a.displayName.localeCompare(b.displayName));
-
- return (
- <>
- {!pipelineType && (
-
- Runtime Platform:
-
-
- {filteredPlatforms.map((p) => (
-
- {p.displayName}
-
- ))}
-
-
- )}
-
- {/* must be present in dom at initial render */}
-
- Runtime Configuration:
-
-
- {configs.map((c) => (
-
- {c.displayName}
-
- ))}
-
-
- {children?.(platform)}
- >
- );
-};
-
-export default RuntimeConfigSelect;
diff --git a/packages/pipeline-editor/schema/src/RuntimeImagesWidget.tsx b/packages/pipeline-editor/schema/src/RuntimeImagesWidget.tsx
deleted file mode 100644
index f77b3f2fd..000000000
--- a/packages/pipeline-editor/schema/src/RuntimeImagesWidget.tsx
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import {
- MetadataWidget,
- IMetadataWidgetProps,
- IMetadata,
- MetadataDisplay,
- IMetadataDisplayProps,
- IMetadataDisplayState,
-} from '@elyra/metadata-common';
-import { IDictionary } from '@elyra/services';
-
-import React from 'react';
-
-export const RUNTIME_IMAGES_SCHEMASPACE = 'runtime-images';
-
-const RUNTIME_IMAGES_CLASS = 'elyra-metadata-runtime-images';
-
-const getLinkFromImageName = (imageName: string): string => {
- let hostname = '';
- const fqinParts = imageName.split('/');
-
- if (
- fqinParts[0].includes('.') ||
- fqinParts[0].includes(':') ||
- fqinParts[0].includes('localhost')
- ) {
- hostname = fqinParts[0];
- imageName = fqinParts.slice(1).join('/');
- }
-
- if (!hostname || hostname.includes('docker.io')) {
- hostname = 'hub.docker.com/r';
- }
-
- const imageRepo = imageName.split(':')[0];
-
- return `https://${hostname}/${imageRepo}`;
-};
-
-/**
- * A React Component for displaying the runtime images list.
- */
-class RuntimeImagesDisplay extends MetadataDisplay<
- IMetadataDisplayProps,
- IMetadataDisplayState
-> {
- renderExpandableContent(metadata: IDictionary): JSX.Element {
- return (
-
- );
- }
-}
-
-/**
- * A widget for displaying runtime images.
- */
-export class RuntimeImagesWidget extends MetadataWidget {
- constructor(props: IMetadataWidgetProps) {
- super(props);
- }
-
- renderDisplay(metadata: IMetadata[]): React.ReactElement {
- if (Array.isArray(metadata) && !metadata.length) {
- // Empty metadata
- return (
-
-
-
- Click the + button to add {this.props.display_name.toLowerCase()}
-
-
- );
- }
- return (
- {
- return 'runtime image';
- }}
- />
- );
- }
-}
diff --git a/packages/pipeline-editor/schema/src/RuntimesWidget.tsx b/packages/pipeline-editor/schema/src/RuntimesWidget.tsx
deleted file mode 100644
index 2842428b8..000000000
--- a/packages/pipeline-editor/schema/src/RuntimesWidget.tsx
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {
- MetadataWidget,
- IMetadataWidgetProps,
- IMetadata,
- MetadataDisplay,
- IMetadataDisplayProps,
- IMetadataDisplayState,
-} from '@elyra/metadata-common';
-import { IDictionary, MetadataService } from '@elyra/services';
-import { RequestErrors } from '@elyra/ui-components';
-import React from 'react';
-
-import {
- IRuntimeType,
- PipelineService,
- RUNTIMES_SCHEMASPACE,
-} from './PipelineService';
-
-const RUNTIMES_METADATA_CLASS = 'elyra-metadata-runtimes';
-
-const addTrailingSlash = (url: string): string => {
- return url.endsWith('/') ? url : url + '/';
-};
-
-const getGithubURLFromAPI = (apiEndpoint: string): string => {
- // For Enterprise Server the api is located at /api/
- let baseURL = new URL(apiEndpoint).origin;
-
- // For Github.com and Github AE the api is located at api.
- baseURL = baseURL.replace('api.', '');
-
- return addTrailingSlash(baseURL);
-};
-
-export interface IRuntimesDisplayProps extends IMetadataDisplayProps {
- metadata: IMetadata[];
- openMetadataEditor: (args: any) => void;
- updateMetadata: () => void;
- schemaspace: string;
- sortMetadata: boolean;
- className: string;
- schemas?: IDictionary[];
- titleContext?: string;
- appendToTitle?: boolean;
-}
-
-/**
- * A React Component for displaying the runtimes list.
- */
-class RuntimesDisplay extends MetadataDisplay<
- IRuntimesDisplayProps,
- IMetadataDisplayState
-> {
- renderExpandableContent(metadata: IDictionary): JSX.Element {
- let apiEndpoint = addTrailingSlash(metadata.metadata.api_endpoint);
- let cosEndpoint = addTrailingSlash(metadata.metadata.cos_endpoint);
-
- let githubRepoElement = null;
- let metadata_props = null;
-
- for (const schema of this.props.schemas ?? []) {
- if (schema.name === metadata.schema_name) {
- metadata_props = schema.properties.metadata.properties;
- }
- }
-
- if (metadata.schema_name === 'airflow' && metadata_props) {
- const githubRepoUrl =
- getGithubURLFromAPI(metadata.metadata.github_api_endpoint) +
- metadata.metadata.github_repo +
- '/tree/' +
- metadata.metadata.github_branch +
- '/';
- githubRepoElement = (
-
- {metadata_props.github_repo.title}
-
- {githubRepoUrl}
-
-
-
-
- );
- }
- if (metadata.schema_name === 'kfp') {
- if (metadata.metadata.public_api_endpoint) {
- // user specified a public API endpoint. use it instead of the API endpoint
- apiEndpoint = addTrailingSlash(metadata.metadata.public_api_endpoint);
- }
- }
-
- if (metadata.metadata.public_cos_endpoint) {
- // user specified a public COS endpoint. use it instead of the API endpoint
- cosEndpoint = addTrailingSlash(metadata.metadata.public_cos_endpoint);
- }
-
- return (
-
-
- {metadata_props ? metadata_props.api_endpoint.title : 'API Endpoint'}
-
-
- {apiEndpoint}
-
-
-
- {githubRepoElement}
-
- {metadata_props
- ? metadata_props.cos_endpoint.title
- : 'Cloud Object Storage'}
-
-
- {cosEndpoint}
-
-
- );
- }
-}
-
-/**
- * A widget for displaying runtimes.
- */
-export class RuntimesWidget extends MetadataWidget {
- runtimeTypes: IRuntimeType[] = [];
-
- constructor(props: IMetadataWidgetProps) {
- super(props);
- }
-
- async fetchMetadata(): Promise {
- return await PipelineService.getRuntimes().catch((error) =>
- RequestErrors.serverError(error),
- );
- }
-
- async getSchemas(): Promise {
- try {
- const schemas = await MetadataService.getSchema(this.props.schemaspace);
- this.runtimeTypes = await PipelineService.getRuntimeTypes();
- const sortedSchema = schemas.sort((a: any, b: any) =>
- a.title.localeCompare(b.title),
- );
- this.schemas = sortedSchema.filter((schema: any) => {
- return !!this.runtimeTypes.find(
- (r) => r.id === schema.runtime_type && r.runtime_enabled,
- );
- });
- if (this.schemas?.length ?? 0 > 1) {
- for (const schema of this.schemas ?? []) {
- this.props.app.contextMenu.addItem({
- selector: `#${this.props.schemaspace} .elyra-metadataHeader-addButton`,
- command: 'elyra-metadata-editor:open',
- args: {
- onSave: this.updateMetadata,
- schemaspace: this.props.schemaspace,
- schema: schema.name,
- title: schema.title,
- titleContext: this.props.titleContext,
- appendToTitle: this.props.appendToTitle,
- } as any,
- });
- }
- }
- this.update();
- } catch (error) {
- RequestErrors.serverError(error);
- }
- }
-
- private getSchemaTitle = (metadata: IMetadata): string => {
- if (this.schemas) {
- for (const schema of this.schemas) {
- if (schema.name === metadata.schema_name) {
- return schema.title;
- }
- }
- }
-
- return 'runtime configuration';
- };
-
- addMetadata(schema: string, titleContext?: string): void {
- this.openMetadataEditor({
- onSave: this.updateMetadata,
- schemaspace: this.props.schemaspace,
- schema: schema,
- titleContext: titleContext,
- });
- }
-
- renderDisplay(metadata: IMetadata[]): React.ReactElement {
- if (Array.isArray(metadata) && !metadata.length) {
- // Empty metadata
- return (
-
-
-
- Click the + button to add {this.props.display_name.toLowerCase()}
-
-
- );
- }
-
- const filteredMetadata = metadata.filter((m) => {
- return !!this.runtimeTypes.find((r) => m.metadata?.runtime_type === r.id);
- });
-
- return (
-
- );
- }
-}
diff --git a/packages/pipeline-editor/schema/src/SubmitFileButtonExtension.tsx b/packages/pipeline-editor/schema/src/SubmitFileButtonExtension.tsx
deleted file mode 100644
index 03f480738..000000000
--- a/packages/pipeline-editor/schema/src/SubmitFileButtonExtension.tsx
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { ContentParser } from '@elyra/services';
-import { RequestErrors, showFormDialog } from '@elyra/ui-components';
-import { Dialog, showDialog, ToolbarButton } from '@jupyterlab/apputils';
-import { PathExt } from '@jupyterlab/coreutils';
-import { DocumentRegistry, DocumentWidget } from '@jupyterlab/docregistry';
-import { IDisposable } from '@lumino/disposable';
-
-import * as React from 'react';
-
-import { FileSubmissionDialog } from './FileSubmissionDialog';
-import { formDialogWidget } from './formDialogWidget';
-import { PipelineService, RUNTIMES_SCHEMASPACE } from './PipelineService';
-import { createRuntimeData, getConfigDetails } from './runtime-utils';
-import Utils from './utils';
-
-/**
- * Submit file button extension
- * - Attach button to editor toolbar and launch a dialog requesting
- * information about the remote location to where submit the file
- * for execution
- */
-
-export class SubmitFileButtonExtension<
- T extends DocumentWidget,
- U extends DocumentRegistry.IModel,
-> implements DocumentRegistry.IWidgetExtension
-{
- showWidget = async (document: T): Promise => {
- const { context } = document;
- if (context.model.dirty) {
- const dialogResult = await showDialog({
- title:
- 'This file contains unsaved changes. To run the file as pipeline the changes need to be saved.',
- buttons: [
- Dialog.cancelButton(),
- Dialog.okButton({ label: 'Save and Submit' }),
- ],
- });
- if (dialogResult.button.accept === false) {
- return;
- }
- await context.save();
- }
-
- const env = await ContentParser.getEnvVars(context.path).catch((error) =>
- RequestErrors.serverError(error),
- );
- const runtimeTypes: any = await PipelineService.getRuntimeTypes().catch(
- (error) => RequestErrors.serverError(error),
- );
- const runtimes = await PipelineService.getRuntimes()
- .then((runtimeList) => {
- return runtimeList.filter((runtime: any) => {
- return (
- !runtime.metadata.runtime_enabled &&
- !!runtimeTypes.find(
- (r: any) => runtime.metadata.runtime_type === r.id,
- )
- );
- });
- })
- .catch((error) => RequestErrors.serverError(error));
- const images = await PipelineService.getRuntimeImages().catch((error) =>
- RequestErrors.serverError(error),
- );
- const schema = await PipelineService.getRuntimesSchema().catch((error) =>
- RequestErrors.serverError(error),
- );
-
- const runtimeData = createRuntimeData({ schema, runtimes });
-
- if (!runtimeData.platforms.find((p) => p.configs.length > 0)) {
- const res = await RequestErrors.noMetadataError(
- 'runtime',
- `run file as pipeline.`,
- );
-
- if (res.button.label.includes(RUNTIMES_SCHEMASPACE)) {
- // Open the runtimes widget
- Utils.getLabShell(document).activateById(
- `elyra-metadata:${RUNTIMES_SCHEMASPACE}`,
- );
- }
- return;
- }
-
- let dependencyFileExtension = PathExt.extname(context.path);
- if (dependencyFileExtension === '.ipynb') {
- dependencyFileExtension = '.py';
- }
-
- const dialogOptions = {
- title: 'Run file as pipeline',
- body: formDialogWidget(
- ,
- ),
- buttons: [Dialog.cancelButton(), Dialog.okButton()],
- };
-
- const dialogResult = await showFormDialog(dialogOptions);
-
- if (dialogResult.value === null) {
- // When Cancel is clicked on the dialog, just return
- return;
- }
-
- const {
- runtime_config,
- framework,
- cpu,
- gpu,
- memory,
- dependency_include,
- dependencies,
- ...envObject
- } = dialogResult.value;
-
- const configDetails = getConfigDetails(runtimeData, runtime_config);
-
- // prepare file submission details
- const pipeline = Utils.generateSingleFilePipeline(
- context.path,
- configDetails,
- framework,
- dependency_include ? dependencies.split(',') : undefined,
- envObject,
- cpu,
- gpu,
- memory,
- );
-
- PipelineService.submitPipeline(
- pipeline,
- configDetails?.platform.displayName ?? '',
- ).catch((error) => RequestErrors.serverError(error));
- };
-
- createNew(editor: T): IDisposable {
- // Create the toolbar button
- const submitFileButton = new ToolbarButton({
- label: 'Run as Pipeline',
- onClick: (): any => this.showWidget(editor),
- tooltip: 'Run file as batch',
- });
-
- // Add the toolbar button to the editor
- editor.toolbar.insertItem(10, 'submitFile', submitFileButton);
-
- // The ToolbarButton class implements `IDisposable`, so the
- // button *is* the extension for the purposes of this method.
- return submitFileButton;
- }
-}
diff --git a/packages/pipeline-editor/schema/src/dialogs.tsx b/packages/pipeline-editor/schema/src/dialogs.tsx
deleted file mode 100644
index b6a026bb7..000000000
--- a/packages/pipeline-editor/schema/src/dialogs.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { Dialog } from '@jupyterlab/apputils';
-import React from 'react';
-
-export const unknownError = (message: string): any => ({
- title: 'Load pipeline failed!',
- body: message,
- buttons: [Dialog.okButton()],
-});
-
-export const elyraOutOfDate = {
- title: 'Load pipeline failed!',
- body: `This pipeline corresponds to a more recent version of Elyra and cannot be used until Elyra has been upgraded.`,
- buttons: [Dialog.okButton()],
-};
-
-export const unsupportedVersion = {
- title: 'Load pipeline failed!',
- body: 'This pipeline has an unrecognizable version.',
- buttons: [Dialog.okButton()],
-};
-
-export const pipelineOutOfDate = {
- title: 'Migrate pipeline?',
- body: (
-
- This pipeline corresponds to an older version of Elyra and needs to be
- migrated.
-
- Although the pipeline can be further edited and/or submitted after its
- update,
-
- the migration will not be completed until the pipeline has been saved
- within the editor.
-
-
- Proceed with migration?
-
- ),
- buttons: [Dialog.cancelButton(), Dialog.okButton()],
-};
-
-export const unsupportedFile = {
- title: 'Unsupported File(s)',
- body: 'Only supported files have been added to the pipeline.',
- buttons: [Dialog.okButton()],
-};
-
-export const clearPipeline = {
- title: 'Clear Pipeline',
- body: 'Are you sure you want to clear the pipeline?',
- buttons: [Dialog.cancelButton(), Dialog.okButton({ label: 'Clear' })],
-};
diff --git a/packages/pipeline-editor/schema/src/formDialogWidget.ts b/packages/pipeline-editor/schema/src/formDialogWidget.ts
deleted file mode 100644
index ef762f027..000000000
--- a/packages/pipeline-editor/schema/src/formDialogWidget.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { ReactWidget, Dialog } from '@jupyterlab/apputils';
-import { MessageLoop } from '@lumino/messaging';
-import { Widget } from '@lumino/widgets';
-
-export const formDialogWidget = (
- dialogComponent: JSX.Element,
-): Dialog.IBodyWidget => {
- const widget = ReactWidget.create(dialogComponent) as Dialog.IBodyWidget;
-
- // Immediately update the body even though it has not yet attached in
- // order to trigger a render of the DOM nodes from the React element.
- MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
-
- widget.getValue = (): any => {
- const form = widget.node.querySelector('form');
- const formValues: { [key: string]: any } = {};
- for (const element of Object.values(
- form?.elements ?? [],
- ) as HTMLInputElement[]) {
- switch (element.type) {
- case 'checkbox':
- formValues[element.name] = element.checked;
- break;
- default:
- formValues[element.name] = element.value;
- break;
- }
- }
- return formValues;
- };
-
- return widget;
-};
diff --git a/packages/pipeline-editor/schema/src/index.ts b/packages/pipeline-editor/schema/src/index.ts
deleted file mode 100644
index 92a33f2c0..000000000
--- a/packages/pipeline-editor/schema/src/index.ts
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { PIPELINE_CURRENT_VERSION } from '@elyra/pipeline-editor';
-import { RequestHandler } from '@elyra/services';
-import {
- containerIcon,
- pipelineIcon,
- RequestErrors,
- runtimesIcon,
- componentCatalogIcon,
-} from '@elyra/ui-components';
-
-import {
- JupyterFrontEnd,
- JupyterFrontEndPlugin,
- ILayoutRestorer,
-} from '@jupyterlab/application';
-import { ICommandPalette, WidgetTracker } from '@jupyterlab/apputils';
-import { DocumentWidget } from '@jupyterlab/docregistry';
-import { IFileBrowserFactory } from '@jupyterlab/filebrowser';
-import { ILauncher } from '@jupyterlab/launcher';
-import { IMainMenu } from '@jupyterlab/mainmenu';
-import { ISettingRegistry } from '@jupyterlab/settingregistry';
-import {
- addIcon,
- IRankedMenu,
- LabIcon,
- refreshIcon,
-} from '@jupyterlab/ui-components';
-
-import {
- COMPONENT_CATALOGS_SCHEMASPACE,
- ComponentCatalogsWidget,
-} from './ComponentCatalogsWidget';
-import { PipelineEditorFactory, commandIDs } from './PipelineEditorWidget';
-import { PipelineService, RUNTIMES_SCHEMASPACE } from './PipelineService';
-import {
- RUNTIME_IMAGES_SCHEMASPACE,
- RuntimeImagesWidget,
-} from './RuntimeImagesWidget';
-import { RuntimesWidget } from './RuntimesWidget';
-import { SubmitFileButtonExtension } from './SubmitFileButtonExtension';
-
-import '../style/index.css';
-
-const PIPELINE_EDITOR = 'Pipeline Editor';
-const PIPELINE = 'pipeline';
-const PIPELINE_EDITOR_NAMESPACE = 'elyra-pipeline-editor-extension';
-const PLUGIN_ID = '@elyra/pipeline-editor-extension:plugin';
-
-const createRemoteIcon = async ({
- name,
- url,
-}: {
- name: string;
- url: string;
-}): Promise => {
- const svgstr = await RequestHandler.makeServerRequest(url, {
- method: 'GET',
- type: 'text',
- });
- return new LabIcon({ name, svgstr });
-};
-
-/**
- * Initialization data for the pipeline-editor-extension extension.
- */
-const extension: JupyterFrontEndPlugin = {
- id: PIPELINE,
- autoStart: true,
- requires: [
- ICommandPalette,
- ILauncher,
- IFileBrowserFactory,
- ILayoutRestorer,
- IMainMenu,
- ISettingRegistry,
- ],
- activate: async (
- app: JupyterFrontEnd,
- palette: ICommandPalette,
- launcher: ILauncher,
- browserFactory: IFileBrowserFactory,
- restorer: ILayoutRestorer,
- menu: IMainMenu,
- registry: ISettingRegistry,
- ) => {
- console.log('Elyra - pipeline-editor extension is activated!');
-
- // Fetch the initial state of the settings.
- const settings = await registry
- .load(PLUGIN_ID)
- .catch((error) => console.log(error));
-
- // Set up new widget Factory for .pipeline files
- const pipelineEditorFactory = new PipelineEditorFactory({
- name: PIPELINE_EDITOR,
- fileTypes: [PIPELINE],
- defaultFor: [PIPELINE],
- shell: app.shell,
- commands: app.commands,
- browserFactory: browserFactory,
- serviceManager: app.serviceManager,
- settings: settings,
- });
-
- // Add the default behavior of opening the widget for .pipeline files
- app.docRegistry.addFileType(
- {
- name: PIPELINE,
- displayName: 'Pipeline',
- extensions: ['.pipeline'],
- icon: pipelineIcon,
- },
- ['JSON'],
- );
- app.docRegistry.addWidgetFactory(pipelineEditorFactory);
-
- const tracker = new WidgetTracker({
- namespace: PIPELINE_EDITOR_NAMESPACE,
- });
-
- pipelineEditorFactory.widgetCreated.connect((sender, widget) => {
- void tracker.add(widget);
-
- // Notify the widget tracker if restore data needs to update
- widget.context.pathChanged.connect(() => {
- void tracker.save(widget);
- });
- });
-
- // Handle state restoration
- void restorer.restore(tracker, {
- command: commandIDs.openDocManager,
- args: (widget) => ({
- path: widget.context.path,
- factory: PIPELINE_EDITOR,
- }),
- name: (widget) => widget.context.path,
- });
-
- // Add command to add file to pipeline
- const addFileToPipelineCommand: string = commandIDs.addFileToPipeline;
- app.commands.addCommand(addFileToPipelineCommand, {
- label: 'Add File to Pipeline',
- icon: addIcon,
- execute: (args) => {
- pipelineEditorFactory.addFileToPipelineSignal.emit(args);
- },
- });
- const refreshPaletteCommand: string = commandIDs.refreshPalette;
- app.commands.addCommand(refreshPaletteCommand, {
- label: 'Refresh Pipeline Palette',
- icon: refreshIcon,
- execute: (args) => {
- pipelineEditorFactory.refreshPaletteSignal.emit(args);
- },
- });
- app.contextMenu.addItem({
- selector: '[data-file-type="notebook"]',
- command: addFileToPipelineCommand,
- });
- app.contextMenu.addItem({
- selector: '[data-file-type="python"]',
- command: addFileToPipelineCommand,
- });
- app.contextMenu.addItem({
- selector: '[data-file-type="r"]',
- command: addFileToPipelineCommand,
- });
-
- // Add an application command
- const openPipelineEditorCommand: string = commandIDs.openPipelineEditor;
- app.commands.addCommand(openPipelineEditorCommand, {
- label: (args: any) => {
- if (args.isPalette) {
- return `New ${PIPELINE_EDITOR}`;
- }
- if (args.runtimeType?.id === 'LOCAL') {
- return `Generic ${PIPELINE_EDITOR}`;
- }
- if (args.isMenu) {
- return `${args.runtimeType?.display_name} ${PIPELINE_EDITOR}`;
- }
- return PIPELINE_EDITOR;
- },
- caption: (args: any) => {
- if (args.runtimeType?.id === 'LOCAL') {
- return `Generic ${PIPELINE_EDITOR}`;
- }
- return `${args.runtimeType?.display_name} ${PIPELINE_EDITOR}`;
- },
- iconLabel: (args: any) => {
- if (args.isPalette) {
- return '';
- }
- if (args.runtimeType?.id === 'LOCAL') {
- return `Generic ${PIPELINE_EDITOR}`;
- }
- return `${args.runtimeType?.display_name} ${PIPELINE_EDITOR}`;
- },
- icon: (args: any) => {
- if (args.isPalette) {
- return undefined;
- }
- return args.runtimeType?.icon;
- },
- execute: (args: any) => {
- // Creates blank file, then opens it in a new window
- app.commands
- .execute(commandIDs.newDocManager, {
- type: 'file',
- path: browserFactory.defaultBrowser.model.path,
- ext: '.pipeline',
- })
- .then(async (model) => {
- const platformId = args.runtimeType?.id;
- const runtime_type =
- platformId === 'LOCAL' ? undefined : platformId;
-
- const pipelineJson = {
- doc_type: 'pipeline',
- version: '3.0',
- json_schema:
- 'http://api.dataplatform.ibm.com/schemas/common-pipeline/pipeline-flow/pipeline-flow-v3-schema.json',
- id: 'elyra-auto-generated-pipeline',
- primary_pipeline: 'primary',
- pipelines: [
- {
- id: 'primary',
- nodes: [],
- app_data: {
- ui_data: {
- comments: [],
- },
- version: PIPELINE_CURRENT_VERSION,
- runtime_type,
- },
- runtime_ref: '',
- },
- ],
- schemas: [],
- };
- const newWidget = await app.commands.execute(
- commandIDs.openDocManager,
- {
- path: model.path,
- factory: PIPELINE_EDITOR,
- },
- );
- newWidget.context.ready.then(() => {
- newWidget.context.model.fromJSON(pipelineJson);
- app.commands.execute(commandIDs.saveDocManager, {
- path: model.path,
- });
- });
- });
- },
- });
- // Add the command to the palette.
- palette.addItem({
- command: openPipelineEditorCommand,
- args: { isPalette: true },
- category: 'Elyra',
- });
-
- PipelineService.getRuntimeTypes()
- .then(async (types) => {
- const filteredTypes = types.filter((t) => t.runtime_enabled);
- const promises = filteredTypes.map(async (t) => {
- return {
- ...t,
- icon: await createRemoteIcon({
- name: `elyra:platform:${t.id}`,
- url: t.icon,
- }),
- };
- });
-
- const resolvedTypes = await Promise.all(promises);
-
- // Add the command to the launcher
- if (launcher) {
- const fileMenuItems: IRankedMenu.IItemOptions[] = [];
-
- for (const t of resolvedTypes as any) {
- launcher.add({
- command: openPipelineEditorCommand,
- category: 'Elyra',
- args: { runtimeType: t },
- rank: t.id === 'LOCAL' ? 1 : 2,
- });
-
- fileMenuItems.push({
- command: openPipelineEditorCommand,
- args: { runtimeType: t, isMenu: true },
- rank: t.id === 'LOCAL' ? 90 : 91,
- });
- }
-
- menu.fileMenu.newMenu.addGroup(fileMenuItems);
- }
- })
- .catch((error) => RequestErrors.serverError(error));
-
- // SubmitNotebookButtonExtension initialization code
- const notebookButtonExtension = new SubmitFileButtonExtension();
- app.docRegistry.addWidgetExtension('Notebook', notebookButtonExtension);
- app.contextMenu.addItem({
- selector: '.jp-Notebook',
- command: commandIDs.submitNotebook,
- rank: -0.5,
- });
-
- // SubmitScriptButtonExtension initialization code
- const scriptButtonExtension = new SubmitFileButtonExtension();
- app.docRegistry.addWidgetExtension('Python Editor', scriptButtonExtension);
- app.contextMenu.addItem({
- selector: '.elyra-ScriptEditor',
- command: commandIDs.submitScript,
- rank: -0.5,
- });
-
- app.docRegistry.addWidgetExtension('R Editor', scriptButtonExtension);
- app.contextMenu.addItem({
- selector: '.elyra-ScriptEditor',
- command: commandIDs.submitScript,
- rank: -0.5,
- });
-
- const runtimesWidget = new RuntimesWidget({
- app,
- display_name: 'Runtimes',
- schemaspace: RUNTIMES_SCHEMASPACE,
- icon: runtimesIcon,
- titleContext: 'runtime configuration',
- appendToTitle: true,
- });
- const runtimesWidgetID = `elyra-metadata:${RUNTIMES_SCHEMASPACE}`;
- runtimesWidget.id = runtimesWidgetID;
- runtimesWidget.title.icon = runtimesIcon;
- runtimesWidget.title.caption = 'Runtimes';
-
- restorer.add(runtimesWidget, runtimesWidgetID);
- app.shell.add(runtimesWidget, 'left', { rank: 950 });
-
- const runtimeImagesWidget = new RuntimeImagesWidget({
- app,
- display_name: 'Runtime Images',
- schemaspace: RUNTIME_IMAGES_SCHEMASPACE,
- icon: containerIcon,
- titleContext: 'runtime image',
- });
- const runtimeImagesWidgetID = `elyra-metadata:${RUNTIME_IMAGES_SCHEMASPACE}`;
- runtimeImagesWidget.id = runtimeImagesWidgetID;
- runtimeImagesWidget.title.icon = containerIcon;
- runtimeImagesWidget.title.caption = 'Runtime Images';
-
- restorer.add(runtimeImagesWidget, runtimeImagesWidgetID);
- app.shell.add(runtimeImagesWidget, 'left', { rank: 951 });
-
- const componentCatalogWidget = new ComponentCatalogsWidget({
- app,
- display_name: 'Component Catalogs', // TODO: This info should come from the server for all schemaspaces
- schemaspace: COMPONENT_CATALOGS_SCHEMASPACE,
- icon: componentCatalogIcon,
- titleContext: 'component catalog',
- refreshCallback: (): void => {
- app.commands.execute(commandIDs.refreshPalette);
- },
- });
- const componentCatalogWidgetID = `elyra-metadata:${COMPONENT_CATALOGS_SCHEMASPACE}`;
- componentCatalogWidget.id = componentCatalogWidgetID;
- componentCatalogWidget.title.icon = componentCatalogIcon;
- componentCatalogWidget.title.caption = 'Component Catalogs';
-
- restorer.add(componentCatalogWidget, componentCatalogWidgetID);
- app.shell.add(componentCatalogWidget, 'left', { rank: 961 });
- },
-};
-export default extension;
diff --git a/packages/pipeline-editor/schema/src/pipeline-hooks.ts b/packages/pipeline-editor/schema/src/pipeline-hooks.ts
deleted file mode 100644
index 163d577fa..000000000
--- a/packages/pipeline-editor/schema/src/pipeline-hooks.ts
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { MetadataService, RequestHandler } from '@elyra/services';
-import { URLExt } from '@jupyterlab/coreutils';
-import { ServerConnection } from '@jupyterlab/services';
-import produce from 'immer';
-import useSWR from 'swr';
-
-import { PipelineService } from './PipelineService';
-
-export const GENERIC_CATEGORY_ID = 'Elyra';
-
-interface IReturn {
- data?: T | undefined;
- error?: any;
- mutate?: any;
-}
-
-type IRuntimeImagesResponse = IRuntimeImage[];
-
-interface IRuntimeImage {
- name: string;
- display_name: string;
- metadata: {
- image_name: string;
- };
-}
-
-const metadataFetcher = async (key: string): Promise => {
- return await MetadataService.getMetadata(key);
-};
-
-export const useRuntimeImages = (): IReturn => {
- const { data, error } = useSWR(
- 'runtime-images',
- metadataFetcher,
- );
-
- data?.sort((a, b) => 0 - (a.name > b.name ? -1 : 1));
-
- return { data, error };
-};
-
-const schemaFetcher = async (key: string): Promise => {
- return await MetadataService.getSchema(key);
-};
-
-// TODO: type this
-export const useRuntimesSchema = (): IReturn => {
- const { data, error } = useSWR('runtimes', schemaFetcher);
-
- return { data, error };
-};
-
-interface IRuntimeComponentsResponse {
- version: string;
- categories: IRuntimeComponent[];
- properties: IComponentPropertiesResponse;
- parameters: IComponentPropertiesResponse;
-}
-
-export interface IRuntimeComponent {
- label: string;
- image: string;
- id: string;
- description: string;
- runtime?: string;
- node_types: {
- op: string;
- id: string;
- label: string;
- image: string;
- runtime_type?: string;
- type: 'execution_node';
- inputs: { app_data: any }[];
- outputs: { app_data: any }[];
- app_data: any;
- }[];
- extensions?: string[];
-}
-
-interface IComponentPropertiesResponse {
- current_parameters: { [key: string]: any };
- parameters: { id: string }[];
- uihints: {
- parameter_info: {
- parameter_ref: string;
- control: 'custom';
- custom_control_id: string;
- label: { default: string };
- description: {
- default: string;
- placement: 'on_panel';
- };
- data: any;
- }[];
- };
- group_info: {
- group_info: {
- id: string;
- parameter_refs: string[];
- }[];
- }[];
-}
-
-/**
- * Sort palette in place. Takes a list of categories each containing a list of
- * components.
- * - Categories: alphabetically by "label" (exception: "generic" always first)
- * - Components: alphabetically by "op" (where is component label stored?)
- */
-export const sortPalette = (palette: {
- categories: IRuntimeComponent[];
-}): void => {
- palette.categories.sort((a, b) => {
- if (a.id === GENERIC_CATEGORY_ID) {
- return -1;
- }
- if (b.id === GENERIC_CATEGORY_ID) {
- return 1;
- }
- return a.label.localeCompare(b.label, undefined, { numeric: true });
- });
-
- for (const components of palette.categories) {
- components.node_types.sort((a, b) =>
- a.label.localeCompare(b.label, undefined, {
- numeric: true,
- }),
- );
- }
-};
-
-// TODO: This should be enabled through `extensions`
-const NodeIcons: Map = new Map([
- ['execute-notebook-node', 'static/elyra/notebook.svg'],
- ['execute-python-node', 'static/elyra/python.svg'],
- ['execute-r-node', 'static/elyra/r-logo.svg'],
-]);
-
-// TODO: We should decouple components and properties to support lazy loading.
-// TODO: type this
-export const componentFetcher = async (type: string): Promise => {
- const palettePromise =
- RequestHandler.makeGetRequest(
- `elyra/pipeline/components/${type}`,
- );
-
- const pipelinePropertiesPromise =
- RequestHandler.makeGetRequest(
- `elyra/pipeline/${type}/properties`,
- );
-
- const pipelineParametersPromise =
- RequestHandler.makeGetRequest(
- `elyra/pipeline/${type}/parameters`,
- );
-
- const typesPromise = PipelineService.getRuntimeTypes();
-
- const [palette, pipelineProperties, pipelineParameters, types] =
- await Promise.all([
- palettePromise,
- pipelinePropertiesPromise,
- pipelineParametersPromise,
- typesPromise,
- ]);
-
- palette.properties = pipelineProperties;
- palette.parameters = pipelineParameters;
-
- // Gather list of component IDs to fetch properties for.
- const componentList: string[] = [];
- for (const category of palette.categories) {
- for (const node of category.node_types) {
- componentList.push(node.id);
- }
- }
-
- const propertiesPromises = componentList.map(async (componentID) => {
- const res =
- await RequestHandler.makeGetRequest(
- `elyra/pipeline/components/${type}/${componentID}/properties`,
- );
- return {
- id: componentID,
- properties: res,
- };
- });
-
- // load all of the properties in parallel instead of serially
- const properties = await Promise.all(propertiesPromises);
-
- // inject properties
- for (const category of palette.categories) {
- // Use the runtime_type from the first node of the category to determine category
- // icon.
- // TODO: Ideally, this would be included in the category.
- const category_runtime_type =
- category.node_types?.[0]?.runtime_type ?? 'LOCAL';
-
- const type = types.find((t: any) => t.id === category_runtime_type);
- const baseUrl = ServerConnection.makeSettings().baseUrl;
- const defaultIcon = URLExt.parse(
- URLExt.join(baseUrl, type?.icon || ''),
- ).pathname;
-
- category.image = defaultIcon;
-
- for (const node of category.node_types) {
- // update icon
- const genericNodeIcon = NodeIcons.get(node.op);
- const nodeIcon = genericNodeIcon
- ? URLExt.parse(URLExt.join(baseUrl, genericNodeIcon)).pathname
- : defaultIcon;
-
- // Not sure which is needed...
- node.image = nodeIcon;
- node.app_data.image = nodeIcon;
- node.app_data.ui_data.image = nodeIcon;
-
- const prop = properties.find((p) => p.id === node.id);
- node.app_data.properties = prop?.properties;
- }
- }
-
- sortPalette(palette);
-
- return palette;
-};
-
-const updateRuntimeImages = (
- properties: any,
- runtimeImages: IRuntimeImage[] | undefined,
-): void => {
- const runtimeImageProperties =
- properties?.properties?.component_parameters?.properties?.runtime_image ??
- properties?.properties?.pipeline_defaults?.properties?.runtime_image;
-
- const imageNames = (runtimeImages ?? []).map((i) => i.metadata.image_name);
-
- const displayNames: { [key: string]: string } = {};
-
- (runtimeImages ?? []).forEach((i: IRuntimeImage) => {
- displayNames[i.metadata.image_name] = i.display_name;
- });
-
- if (runtimeImageProperties) {
- runtimeImageProperties.enumNames = (runtimeImages ?? []).map(
- (i) => i.display_name,
- );
- runtimeImageProperties.enum = imageNames;
- }
-};
-
-export const usePalette = (type = 'local'): IReturn => {
- const { data: runtimeImages, error: runtimeError } = useRuntimeImages();
-
- const {
- data: palette,
- error: paletteError,
- mutate: mutate,
- } = useSWR(type, componentFetcher);
-
- let updatedPalette;
- if (palette !== undefined) {
- updatedPalette = produce(palette, (draft: any) => {
- for (const category of draft.categories) {
- for (const node of category.node_types) {
- // update runtime images
- updateRuntimeImages(node.app_data.properties, runtimeImages);
- }
- }
- updateRuntimeImages(draft.properties, runtimeImages);
- });
- }
-
- return {
- data: updatedPalette,
- error: runtimeError ?? paletteError,
- mutate: mutate,
- };
-};
diff --git a/packages/pipeline-editor/schema/src/runtime-utils.ts b/packages/pipeline-editor/schema/src/runtime-utils.ts
deleted file mode 100644
index c4cf0bffc..000000000
--- a/packages/pipeline-editor/schema/src/runtime-utils.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { IRuntime, ISchema } from './PipelineService';
-
-export interface IRuntimeData {
- platforms: {
- id: string;
- displayName: string;
- configs: {
- id: string;
- displayName: string;
- processor: {
- id: string;
- };
- }[];
- }[];
- allowLocal: boolean;
-}
-
-export const createRuntimeData = ({
- runtimes,
- schema,
- allowLocal,
-}: {
- runtimes: IRuntime[];
- schema: ISchema[];
- allowLocal?: boolean;
-}): IRuntimeData => {
- const platforms: IRuntimeData['platforms'] = [];
- for (const s of schema) {
- const found = platforms.find((p) => p.id === s.runtime_type);
- if (found) {
- continue;
- }
- platforms.push({
- id: s.runtime_type,
- displayName: s.title,
- configs: runtimes
- .filter((r) => r.metadata.runtime_type === s.runtime_type)
- .map((r) => ({
- id: r.name,
- displayName: r.display_name,
- processor: {
- id: r.schema_name,
- },
- })),
- });
- }
- return { platforms, allowLocal: !!allowLocal };
-};
-
-export interface IConfigDetails {
- id: string;
- displayName: string;
- platform: {
- id: string;
- displayName: string;
- };
- processor: {
- id: string;
- };
-}
-
-export const getConfigDetails = (
- runtimeData: IRuntimeData,
- configId: string,
-): IConfigDetails | undefined => {
- for (const platform of runtimeData.platforms) {
- for (const config of platform.configs) {
- if (config.id === configId) {
- return {
- id: config.id,
- displayName: config.displayName,
- platform: {
- id: platform.id,
- displayName: platform.displayName,
- },
- processor: {
- id: config.processor.id,
- },
- };
- }
- }
- }
- return undefined;
-};
diff --git a/packages/pipeline-editor/schema/src/test/pipeline-hooks.spec.ts b/packages/pipeline-editor/schema/src/test/pipeline-hooks.spec.ts
deleted file mode 100644
index 9319babbe..000000000
--- a/packages/pipeline-editor/schema/src/test/pipeline-hooks.spec.ts
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {
- IRuntimeComponent,
- GENERIC_CATEGORY_ID,
- sortPalette,
-} from '../pipeline-hooks';
-
-const GENERIC_CATEGORY = {
- label: 'ZZZZ should not matter',
- image: 'string',
- id: GENERIC_CATEGORY_ID,
- description: 'string',
- node_types: [],
-};
-
-type Category = IRuntimeComponent;
-type Component = Category['node_types'][0];
-
-const createMockCategory = (
- name: string,
- components: Component[] = [],
-): Category => {
- return {
- label: name,
- image: 'string',
- id: 'string',
- description: 'string',
- node_types: components,
- };
-};
-
-const createMockComponent = (name: string): Component => {
- return {
- op: 'string',
- label: name,
- id: 'string',
- image: 'string',
- type: 'execution_node',
- inputs: [],
- outputs: [],
- app_data: {},
- };
-};
-
-describe('sortPalette', () => {
- it('should function with no categories', () => {
- const palette = { categories: [] };
- const expected = { categories: [] };
- sortPalette(palette);
- expect(palette).toStrictEqual(expected);
- });
-
- it('should sort categories alphabetically', () => {
- const palette = {
- categories: [
- createMockCategory('a'),
- createMockCategory('c'),
- createMockCategory('b'),
- ],
- };
- const expected = {
- categories: [
- createMockCategory('a'),
- createMockCategory('b'),
- createMockCategory('c'),
- ],
- };
- sortPalette(palette);
- expect(palette).toStrictEqual(expected);
- });
-
- it('should sort components alphabetically', () => {
- const palette = {
- categories: [
- createMockCategory('a', [
- createMockComponent('c'),
- createMockComponent('a'),
- createMockComponent('b'),
- ]),
- ],
- };
- const expected = {
- categories: [
- createMockCategory('a', [
- createMockComponent('a'),
- createMockComponent('b'),
- createMockComponent('c'),
- ]),
- ],
- };
- sortPalette(palette);
- expect(palette).toStrictEqual(expected);
- });
-
- it('should sort categories numerically', () => {
- const palette = {
- categories: [
- createMockCategory('c200'),
- createMockCategory('c2'),
- createMockCategory('c20'),
- createMockCategory('c100'),
- createMockCategory('c1'),
- createMockCategory('c10'),
- ],
- };
- const expected = {
- categories: [
- createMockCategory('c1'),
- createMockCategory('c2'),
- createMockCategory('c10'),
- createMockCategory('c20'),
- createMockCategory('c100'),
- createMockCategory('c200'),
- ],
- };
- sortPalette(palette);
- expect(palette).toStrictEqual(expected);
- });
-
- it('should sort components numerically', () => {
- const palette = {
- categories: [
- createMockCategory('a', [
- createMockComponent('c200'),
- createMockComponent('c2'),
- createMockComponent('c20'),
- createMockComponent('c100'),
- createMockComponent('c1'),
- createMockComponent('c10'),
- ]),
- ],
- };
- const expected = {
- categories: [
- createMockCategory('a', [
- createMockComponent('c1'),
- createMockComponent('c2'),
- createMockComponent('c10'),
- createMockComponent('c20'),
- createMockComponent('c100'),
- createMockComponent('c200'),
- ]),
- ],
- };
- sortPalette(palette);
- expect(palette).toStrictEqual(expected);
- });
-
- it('should sort generic category first', () => {
- const palette = {
- categories: [
- GENERIC_CATEGORY,
- createMockCategory('a'),
- createMockCategory('c'),
- createMockCategory('b'),
- ],
- };
- const expected = {
- categories: [
- GENERIC_CATEGORY,
- createMockCategory('a'),
- createMockCategory('b'),
- createMockCategory('c'),
- ],
- };
- sortPalette(palette);
- expect(palette).toStrictEqual(expected);
- });
-});
diff --git a/packages/pipeline-editor/schema/src/test/pipeline-service.spec.ts b/packages/pipeline-editor/schema/src/test/pipeline-service.spec.ts
deleted file mode 100644
index a9dd6baf6..000000000
--- a/packages/pipeline-editor/schema/src/test/pipeline-service.spec.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { JupyterServer } from '@jupyterlab/testutils';
-
-import { PipelineService } from '../PipelineService';
-jest.setTimeout(3 * 60 * 1000);
-
-const server = new JupyterServer();
-
-beforeAll(async () => {
- await server.start();
-});
-
-afterAll(async () => {
- await server.shutdown();
-});
-
-describe('@elyra/pipeline-editor', () => {
- describe('PipelineService', () => {
- describe('#getRuntimeTypes', () => {
- it('should get runtimes ordered by id', async () => {
- const runtime_types = await PipelineService.getRuntimeTypes();
- const expected_runtime_ids = [
- 'APACHE_AIRFLOW',
- 'KUBEFLOW_PIPELINES',
- 'LOCAL',
- ];
- expect(
- runtime_types.map((runtime_type) => runtime_type.id),
- ).toStrictEqual(expected_runtime_ids);
- });
- });
- });
-});
diff --git a/packages/pipeline-editor/schema/src/theme.tsx b/packages/pipeline-editor/schema/src/theme.tsx
deleted file mode 100644
index 57c974548..000000000
--- a/packages/pipeline-editor/schema/src/theme.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { trashIcon } from '@elyra/ui-components';
-import {
- closeIcon,
- caretDownEmptyIcon,
- editIcon,
- folderIcon,
- LabIcon,
- paletteIcon,
-} from '@jupyterlab/ui-components';
-import * as React from 'react';
-
-const SvgIcon: React.FC = ({ children }): any => {
- return (
-
- {children}
-
- );
-};
-
-const theme: any = {
- palette: {
- focus: 'var(--jp-brand-color0)',
- border: 'var(--jp-border-color0)',
- divider: 'var(--jp-border-color0)',
- hover: 'var(--jp-border-color1)',
- active: 'rgba(255, 255, 255, 0.18)',
- inputBorder: 'var(--jp-border-color0)',
- primary: {
- main: 'var(--jp-inverse-layout-color4)',
- hover: 'transparent',
- contrastText: 'var(--jp-layout-color1)',
- },
- secondary: {
- main: 'transparent',
- contrastText: 'var(--jp-content-font-color1)',
- },
- error: {
- main: 'var(--jp-error-color0)',
- contrastText: 'var(--jp-icon-contrast-color3)',
- },
- errorMessage: {
- main: 'var(--jp-error-color1)',
- contrastText: 'rgba(255, 255, 255, 0.9)',
- errorBorder: 'var(--jp-error-color0)',
- },
- icon: {
- primary: 'var(--jp-ui-font-color0)',
- secondary: 'var(--jp-ui-font-color0)',
- },
- text: {
- primary: 'var(--jp-content-font-color0)',
- secondary: 'var(--jp-ui-font-color1)',
- bold: 'var(--jp-inverse-layout-color2)',
- inactive: 'var(--jp-inverse-layout-color4)',
- disabled: 'var(--jp-content-font-color3)',
- link: 'var(--jp-content-link-color)',
- error: 'var(--jp-error-color0)',
- icon: 'var(--jp-inverse-layout-color2)',
- },
- background: {
- default: 'var(--jp-layout-color1)',
- secondary: 'var(--jp-border-color2)',
- input: 'transparent',
- },
- highlight: {
- border: 'transparent',
- hover: 'var(--jp-content-font-color0)',
- focus: 'transparent',
- },
- },
- shape: {
- borderRadius: '4px',
- },
- typography: {
- fontFamily: 'var(--jp-ui-font-family)',
- fontWeight: 'normal',
- fontSize: 'var(--jp-content-font-size1)',
- },
- overrides: {
- deleteIcon: LabIcon.resolveReact({ icon: trashIcon }),
- editIcon: LabIcon.resolveReact({ icon: editIcon }),
- folderIcon: LabIcon.resolveReact({ icon: folderIcon }),
- closeIcon: LabIcon.resolveReact({ icon: closeIcon }),
- propertiesIcon: (
-
-
-
- ),
- paletteIcon: LabIcon.resolveReact({ icon: paletteIcon }),
- checkIcon: (
-
-
-
- ),
- chevronDownIcon: LabIcon.resolveReact({ icon: caretDownEmptyIcon }),
- },
-};
-
-export { theme };
diff --git a/packages/pipeline-editor/schema/src/utils.ts b/packages/pipeline-editor/schema/src/utils.ts
deleted file mode 100644
index 24e438601..000000000
--- a/packages/pipeline-editor/schema/src/utils.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2018-2023 Elyra Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { PIPELINE_CURRENT_VERSION } from '@elyra/pipeline-editor';
-
-import { LabShell } from '@jupyterlab/application';
-import { PathExt } from '@jupyterlab/coreutils';
-
-import uuid4 from 'uuid/v4';
-
-import { IConfigDetails } from './runtime-utils';
-
-/**
- * A utilities class for static functions.
- */
-export default class Utils {
- /**
- * Utility to create a one node pipeline to submit a single file as a pipeline
- */
- static generateSingleFilePipeline(
- filename: string,
- configDetails: IConfigDetails | undefined,
- runtimeImage: string,
- dependencies: string[] | undefined,
- envObject: { [key: string]: string },
- cpu?: number,
- gpu?: number,
- memory?: number,
- ): any {
- const generated_uuid = uuid4();
-
- const artifactName = PathExt.basename(filename, PathExt.extname(filename));
-
- const envVars = Object.entries(envObject).map(
- ([key, val]) => `${key}=${val}`,
- );
-
- return {
- doc_type: 'pipeline',
- version: '3.0',
- json_schema:
- 'http://api.dataplatform.ibm.com/schemas/common-pipeline/pipeline-flow/pipeline-flow-v3-schema.json',
- id: generated_uuid,
- primary_pipeline: generated_uuid,
- pipelines: [
- {
- id: generated_uuid,
- nodes: [
- {
- id: generated_uuid,
- type: 'execution_node',
- op: 'execute-notebook-node',
- app_data: {
- component_parameters: {
- filename,
- runtime_image: runtimeImage,
- outputs: [],
- env_vars: envVars,
- dependencies,
- cpu,
- gpu,
- memory,
- include_subdirectories: false,
- },
- ui_data: {
- label: PathExt.basename(filename),
- },
- },
- },
- ],
- app_data: {
- name: artifactName,
- runtime_config: configDetails?.id,
- version: PIPELINE_CURRENT_VERSION,
- source: PathExt.basename(filename),
- properties: {
- name: 'generic',
- },
- ui_data: {
- comments: [],
- },
- },
- },
- ],
- schemas: [],
- };
- }
-
- /**
- * Break an array into an array of "chunks", each "chunk" having "n" elements.
- * The final "chuck" may have less than "n" elements.
- * Example:
- * chunkArray(['a', 'b', 'c', 'd', 'e', 'f', 'g'], 4)
- * -> [['a', 'b', 'c', 'd'], ['e', 'f', 'g']]
- */
- static chunkArray(arr: T[], n: number): T[][] {
- return Array.from(Array(Math.ceil(arr.length / n)), (_, i) =>
- arr.slice(i * n, i * n + n),
- );
- }
-
- /**
- * From a given widget, find the application shell and return it
- */
- static getLabShell = (widget: any): LabShell => {
- while (widget !== null && !(widget instanceof LabShell)) {
- widget = widget.parent;
- }
-
- return widget;
- };
-}
diff --git a/packages/pipeline-editor/setup.py b/packages/pipeline-editor/setup.py
new file mode 100644
index 000000000..aefdf20db
--- /dev/null
+++ b/packages/pipeline-editor/setup.py
@@ -0,0 +1 @@
+__import__("setuptools").setup()
diff --git a/packages/pipeline-editor/src/ComponentCatalogsWidget.tsx b/packages/pipeline-editor/src/ComponentCatalogsWidget.tsx
index c934203d4..47e503c7e 100644
--- a/packages/pipeline-editor/src/ComponentCatalogsWidget.tsx
+++ b/packages/pipeline-editor/src/ComponentCatalogsWidget.tsx
@@ -21,7 +21,7 @@ import {
MetadataDisplay,
IMetadataDisplayProps,
//IMetadataDisplayState,
- IMetadataActionButton,
+ IMetadataActionButton
} from '@elyra/metadata-common';
import { IDictionary, MetadataService } from '@elyra/services';
import { RequestErrors } from '@elyra/ui-components';
@@ -60,12 +60,12 @@ class ComponentCatalogsDisplay extends MetadataDisplay {
.catch((error) =>
console.error(
'An error occurred while refreshing components from catalog:',
- error,
- ),
+ error
+ )
);
- },
+ }
},
- ...super.actionButtons(metadata),
+ ...super.actionButtons(metadata)
];
}
@@ -141,14 +141,14 @@ export class ComponentCatalogsWidget extends MetadataWidget {
const schemas = await MetadataService.getSchema(this.props.schemaspace);
this.runtimeTypes = await PipelineService.getRuntimeTypes();
const sortedSchema = schemas.sort((a: any, b: any) =>
- a.title.localeCompare(b.title),
+ a.title.localeCompare(b.title)
);
this.schemas = sortedSchema.filter((schema: any) => {
return !!this.runtimeTypes.find(
(r) =>
schema.properties?.metadata?.properties?.runtime_type?.enum?.includes(
- r.id,
- ) && r.runtime_enabled,
+ r.id
+ ) && r.runtime_enabled
);
});
if (this.schemas?.length ?? 0 > 1) {
@@ -162,8 +162,8 @@ export class ComponentCatalogsWidget extends MetadataWidget {
schema: schema.name,
title: schema.title,
titleContext: this.props.titleContext,
- appendToTitle: this.props.appendToTitle,
- } as any,
+ appendToTitle: this.props.appendToTitle
+ } as any
});
}
}
diff --git a/packages/pipeline-editor/src/EmptyPipelineContent.tsx b/packages/pipeline-editor/src/EmptyPipelineContent.tsx
index 3af93d71d..a8c24cd19 100644
--- a/packages/pipeline-editor/src/EmptyPipelineContent.tsx
+++ b/packages/pipeline-editor/src/EmptyPipelineContent.tsx
@@ -28,7 +28,7 @@ export interface IEmptyGenericPipelineProps {
}
export const EmptyGenericPipeline: React.FC = ({
- onOpenSettings,
+ onOpenSettings
}) => {
return (
diff --git a/packages/pipeline-editor/src/FileSubmissionDialog.tsx b/packages/pipeline-editor/src/FileSubmissionDialog.tsx
index 978af1112..7405e9889 100644
--- a/packages/pipeline-editor/src/FileSubmissionDialog.tsx
+++ b/packages/pipeline-editor/src/FileSubmissionDialog.tsx
@@ -63,7 +63,7 @@ export const FileSubmissionDialog: React.FC
= ({
env,
images,
dependencyFileExtension,
- runtimeData,
+ runtimeData
}) => {
const [includeDependency, setIncludeDependency] = React.useState(true);
diff --git a/packages/pipeline-editor/src/ParameterInputForm.tsx b/packages/pipeline-editor/src/ParameterInputForm.tsx
index 45306a1f4..0f800f871 100644
--- a/packages/pipeline-editor/src/ParameterInputForm.tsx
+++ b/packages/pipeline-editor/src/ParameterInputForm.tsx
@@ -31,14 +31,14 @@ export interface IParameterProps {
}
export const ParameterInputForm: React.FC = ({
- parameters,
+ parameters
}) => {
return parameters && parameters.length > 0 ? (
Parameters
@@ -94,9 +94,9 @@ export const ParameterInputForm: React.FC = ({
left: `-${
Math.min(
param.name.length,
- Math.min(DIALOG_WIDTH, param.description.length),
+ Math.min(DIALOG_WIDTH, param.description.length)
) - 4
- }ch`,
+ }ch`
}}
className={'field-description'}
>
diff --git a/packages/pipeline-editor/src/PipelineEditorWidget.tsx b/packages/pipeline-editor/src/PipelineEditorWidget.tsx
index 40d08202e..497429a4e 100644
--- a/packages/pipeline-editor/src/PipelineEditorWidget.tsx
+++ b/packages/pipeline-editor/src/PipelineEditorWidget.tsx
@@ -17,12 +17,12 @@
import {
PipelineEditor,
PipelineOutOfDateError,
- ThemeProvider,
+ ThemeProvider
} from '@elyra/pipeline-editor';
import {
migrate,
validate,
- ComponentNotFoundError,
+ ComponentNotFoundError
} from '@elyra/pipeline-services';
import { ContentParser } from '@elyra/services';
import {
@@ -37,7 +37,7 @@ import {
Dropzone,
RequestErrors,
showFormDialog,
- componentCatalogIcon,
+ componentCatalogIcon
} from '@elyra/ui-components';
import { ILabShell } from '@jupyterlab/application';
import { Dialog, ReactWidget, showDialog } from '@jupyterlab/apputils';
@@ -46,9 +46,10 @@ import {
DocumentRegistry,
ABCWidgetFactory,
DocumentWidget,
- Context,
+ Context
} from '@jupyterlab/docregistry';
import { IDefaultFileBrowser } from '@jupyterlab/filebrowser';
+import { Contents } from '@jupyterlab/services';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import 'carbon-components/css/carbon-components.min.css';
@@ -57,35 +58,43 @@ import { toArray } from '@lumino/algorithm';
import { IDragEvent } from '@lumino/dragdrop';
import { Signal } from '@lumino/signaling';
-import React, { useCallback, useEffect, useRef, useState } from 'react';
+import React, {
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState
+} from 'react';
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import {
EmptyGenericPipeline,
- EmptyPlatformSpecificPipeline,
+ EmptyPlatformSpecificPipeline
} from './EmptyPipelineContent';
import { formDialogWidget } from './formDialogWidget';
import {
usePalette,
useRuntimeImages,
- useRuntimesSchema,
+ useRuntimesSchema
} from './pipeline-hooks';
import { PipelineExportDialog } from './PipelineExportDialog';
import {
PipelineService,
RUNTIMES_SCHEMASPACE,
RUNTIME_IMAGES_SCHEMASPACE,
- COMPONENT_CATALOGS_SCHEMASPACE,
+ COMPONENT_CATALOGS_SCHEMASPACE
} from './PipelineService';
import { PipelineSubmissionDialog } from './PipelineSubmissionDialog';
import {
createRuntimeData,
getConfigDetails,
- IRuntimeData,
+ IRuntimeData
} from './runtime-utils';
import { theme } from './theme';
+import { getEmptyPipelineJson } from './index';
+
const PIPELINE_CLASS = 'elyra-PipelineEditor';
export const commandIDs = {
@@ -98,7 +107,7 @@ export const commandIDs = {
submitNotebook: 'notebook:submit',
addFileToPipeline: 'pipeline-editor:add-node',
refreshPalette: 'pipeline-editor:refresh-palette',
- openViewer: 'elyra-code-viewer:open',
+ openViewer: 'elyra-code-viewer:open'
};
interface IExtendedThemeProviderProps
@@ -142,7 +151,7 @@ const isRuntimeTypeAvailable = (data: IRuntimeData, type?: string): boolean => {
const getDisplayName = (
runtimesSchema: any,
- type?: string,
+ type?: string
): string | undefined => {
if (!type) {
return undefined;
@@ -159,6 +168,7 @@ class PipelineEditorWidget extends ReactWidget {
refreshPaletteSignal: Signal;
context: Context;
settings: ISettingRegistry.ISettings;
+ defaultRuntimeType: string;
constructor(options: any) {
super();
@@ -169,13 +179,21 @@ class PipelineEditorWidget extends ReactWidget {
this.refreshPaletteSignal = options.refreshPaletteSignal;
this.context = options.context;
this.settings = options.settings;
+ this.defaultRuntimeType = options.defaultRuntimeType;
let nullPipeline = this.context.model.toJSON() === null;
+
this.context.model.contentChanged.connect(() => {
if (nullPipeline) {
nullPipeline = false;
this.update();
}
});
+ this.context.fileChanged.connect(() => {
+ if (this.context.model.toJSON() === null) {
+ const pipelineJson = getEmptyPipelineJson(this.defaultRuntimeType);
+ this.context.model.fromString(JSON.stringify(pipelineJson));
+ }
+ });
}
render(): any {
@@ -216,7 +234,7 @@ const PipelineWrapper: React.FC = ({
addFileToPipelineSignal,
refreshPaletteSignal,
settings,
- widgetId,
+ widgetId
}) => {
const ref = useRef(null);
const [loading, setLoading] = useState(true);
@@ -234,11 +252,41 @@ const PipelineWrapper: React.FC = ({
const runtimeDisplayName = getDisplayName(runtimesSchema, type) ?? 'Generic';
+ const filePersistedRuntimeImages = useMemo(() => {
+ const images = [];
+ const pipelineDefaultRuntimeImage =
+ pipeline?.pipelines?.[0]?.app_data?.properties?.pipeline_defaults
+ ?.runtime_image;
+ if (pipelineDefaultRuntimeImage?.length > 0) {
+ images.push(pipelineDefaultRuntimeImage);
+ }
+ const nodes = pipeline?.pipelines?.[0]?.nodes;
+ if (nodes?.length > 0) {
+ for (const node of nodes) {
+ const nodeRuntimeImage =
+ node?.app_data?.component_parameters?.runtime_image;
+ if (nodeRuntimeImage?.length > 0) {
+ images.push(nodeRuntimeImage);
+ }
+ }
+ }
+
+ return images.map((imageName) => {
+ return {
+ name: imageName,
+ display_name: imageName,
+ metadata: {
+ image_name: imageName
+ }
+ };
+ });
+ }, [pipeline]);
+
const {
data: palette,
error: paletteError,
- mutate: mutatePalette,
- } = usePalette(type);
+ mutate: mutatePalette
+ } = usePalette(type, filePersistedRuntimeImages);
useEffect(() => {
const handleMutateSignal = (): void => {
@@ -292,7 +340,7 @@ const PipelineWrapper: React.FC = ({
for (const node of nodes) {
if (node?.app_data?.component_parameters) {
for (const [key, val] of Object.entries(
- node?.app_data?.component_parameters,
+ node?.app_data?.component_parameters
)) {
if (val === null) {
node.app_data.component_parameters[key] = undefined;
@@ -309,7 +357,7 @@ const PipelineWrapper: React.FC = ({
const pipeline_path = contextRef.current.path;
const pipeline_name = PathExt.basename(
pipeline_path,
- PathExt.extname(pipeline_path),
+ PathExt.extname(pipeline_path)
);
pipelineJson.pipelines[0].app_data.properties.name = pipeline_name;
pipelineJson.pipelines[0].app_data.properties.runtime =
@@ -339,13 +387,17 @@ const PipelineWrapper: React.FC = ({
} else if (Array.isArray(data[key])) {
const newArray = [];
for (const i in data[key]) {
- if (typeof data[key][i] === 'object') {
- removeNullValues(data[key][i], true);
- if (Object.keys(data[key][i]).length > 0) {
- newArray.push(data[key][i]);
+ const item = data[key][i];
+ if (item === undefined || item === null || item === '') {
+ continue;
+ }
+ if (typeof item === 'object') {
+ removeNullValues(item, true);
+ if (Object.keys(item).length > 0) {
+ newArray.push(item);
}
- } else if (data[key][i] !== null && data[key][i] !== '') {
- newArray.push(data[key][i]);
+ } else {
+ newArray.push(item);
}
}
data[key] = newArray;
@@ -361,11 +413,11 @@ const PipelineWrapper: React.FC = ({
}
removeNullValues(
pipelineJson?.pipelines?.[0]?.app_data?.properties?.pipeline_defaults ??
- {},
+ {}
);
if (contextRef.current.isReady) {
contextRef.current.model.fromString(
- JSON.stringify(pipelineJson, null, 2),
+ JSON.stringify(pipelineJson, null, 2)
);
}
}, []);
@@ -395,7 +447,7 @@ const PipelineWrapper: React.FC = ({
Proceed with migration?
),
- buttons: [Dialog.cancelButton(), Dialog.okButton()],
+ buttons: [Dialog.cancelButton(), Dialog.okButton()]
}).then(async (result) => {
isDialogAlreadyShowing.current = false;
if (result.button.accept) {
@@ -410,13 +462,13 @@ const PipelineWrapper: React.FC = ({
node.app_data.filename =
PipelineService.getPipelineRelativeNodePath(
contextRef.current.path,
- node.app_data.filename,
+ node.app_data.filename
);
}
return pipeline;
});
contextRef.current.model.fromString(
- JSON.stringify(migratedPipeline, null, 2),
+ JSON.stringify(migratedPipeline, null, 2)
);
} catch (migrationError) {
if (migrationError instanceof ComponentNotFoundError) {
@@ -439,7 +491,7 @@ const PipelineWrapper: React.FC = ({
and try again.
),
- buttons: [Dialog.okButton({ label: 'Close' })],
+ buttons: [Dialog.okButton({ label: 'Close' })]
}).then(() => {
shell.currentWidget?.close();
});
@@ -447,7 +499,7 @@ const PipelineWrapper: React.FC = ({
showDialog({
title: 'Pipeline migration failed!',
body: {(migrationError as Error)?.message || ''}
,
- buttons: [Dialog.okButton()],
+ buttons: [Dialog.okButton()]
}).then(() => {
shell.currentWidget?.close();
});
@@ -461,51 +513,56 @@ const PipelineWrapper: React.FC = ({
showDialog({
title: 'Load pipeline failed!',
body: {error?.message || ''}
,
- buttons: [Dialog.okButton()],
+ buttons: [Dialog.okButton()]
}).then(() => {
isDialogAlreadyShowing.current = false;
shell.currentWidget?.close();
});
}
},
- [shell.currentWidget],
+ [shell.currentWidget]
);
const onFileRequested = async (args: any): Promise => {
- const filename = PipelineService.getWorkspaceRelativeNodePath(
- contextRef.current.path,
- args.filename ?? '',
- );
- if (args.propertyID.includes('dependencies')) {
+ const pipelineFilePath = contextRef.current.path;
+ const contextFilePath = args.filename
+ ? PipelineService.getWorkspaceRelativeNodePath(
+ pipelineFilePath,
+ args.filename
+ )
+ : pipelineFilePath;
+ const contextFolderPath = PathExt.dirname(contextFilePath);
+
+ if (args.propertyID?.includes('dependencies')) {
const res = await showBrowseFileDialog(browserFactory.model.manager, {
multiselect: true,
includeDir: true,
- rootPath: PathExt.dirname(filename),
+ rootPath: contextFolderPath,
filter: (model: any): boolean => {
- return model.path !== filename;
- },
+ return model.path !== pipelineFilePath;
+ }
});
if (res.button.accept && res.value.length) {
- return res.value.map((v: any) => v.path);
+ return res.value;
}
} else {
const res = await showBrowseFileDialog(browserFactory.model.manager, {
- startPath: PathExt.dirname(filename),
- filter: (model: any): boolean => {
- if (args.filters?.File === undefined) {
+ startPath: contextFolderPath,
+ filter: (model: Contents.IModel): boolean => {
+ if (args.filters?.File === undefined || model.type === 'directory') {
return true;
}
const ext = PathExt.extname(model.path);
return args.filters.File.includes(ext);
- },
+ }
});
if (res.button.accept && res.value.length) {
const file = PipelineService.getPipelineRelativeNodePath(
contextRef.current.path,
- res.value[0].path,
+ res.value[0]
);
return [file];
}
@@ -520,13 +577,13 @@ const PipelineWrapper: React.FC = ({
}
const path = PipelineService.getWorkspaceRelativeNodePath(
contextRef.current.path,
- args.component_parameters.filename,
+ args.component_parameters.filename
);
const new_env_vars = await ContentParser.getEnvVars(path).then(
(response: any) =>
response.map((str: string) => {
return { env_var: str };
- }),
+ })
);
const env_vars = args.component_parameters?.env_vars ?? [];
@@ -536,16 +593,16 @@ const PipelineWrapper: React.FC = ({
(new_var: any) =>
!env_vars.some((old_var: any) => {
return old_var.env_var === new_var.env_var;
- }),
- ),
+ })
+ )
];
return {
...args,
component_parameters: {
...args.component_parameters,
- env_vars: merged_env_vars.filter(Boolean),
- },
+ env_vars: merged_env_vars.filter(Boolean)
+ }
};
};
@@ -558,7 +615,7 @@ const PipelineWrapper: React.FC = ({
const componentSourceJson = JSON.parse(componentSource);
dialogBody.push(`catalog_type: ${componentSourceJson.catalog_type}`);
for (const [key, value] of Object.entries(
- componentSourceJson.component_ref,
+ componentSourceJson.component_ref
)) {
dialogBody.push(`${key}: ${value}`);
}
@@ -588,39 +645,39 @@ const PipelineWrapper: React.FC = ({
),
- buttons: [Dialog.okButton()],
+ buttons: [Dialog.okButton()]
});
}
return PipelineService.getComponentDef(type, componentId)
.then((res) => {
const nodeDef = getAllPaletteNodes(palette).find(
- (n) => n.id === componentId,
+ (n) => n.id === componentId
);
commands.execute(commandIDs.openViewer, {
content: res.content,
mimeType: res.mimeType,
- label: nodeDef?.label ?? componentId,
+ label: nodeDef?.label ?? componentId
});
})
.catch((e) => RequestErrors.serverError(e));
},
- [commands, palette, type],
+ [commands, palette, type]
);
const onDoubleClick = (data: any): void => {
for (let i = 0; i < data.selectedObjectIds.length; i++) {
const node = pipeline.pipelines[0].nodes.find(
- (node: any) => node.id === data.selectedObjectIds[i],
+ (node: any) => node.id === data.selectedObjectIds[i]
);
const nodeDef = getAllPaletteNodes(palette).find(
- (n) => n.op === node?.op,
+ (n) => n.op === node?.op
);
if (node?.app_data?.component_parameters?.filename) {
commands.execute(commandIDs.openDocManager, {
path: PipelineService.getWorkspaceRelativeNodePath(
contextRef.current.path,
- node.app_data.component_parameters.filename,
- ),
+ node.app_data.component_parameters.filename
+ )
});
} else if (!nodeDef?.app_data?.parameter_refs?.['filehandler']) {
handleOpenComponentDef(nodeDef?.id, node?.app_data?.component_source);
@@ -635,7 +692,7 @@ const PipelineWrapper: React.FC = ({
const errorMessages = validate(
JSON.stringify(pipelineJson),
getAllPaletteNodes(palette),
- palette.properties,
+ palette.properties
);
if (errorMessages && errorMessages.length > 0) {
let errorMessage = '';
@@ -652,8 +709,8 @@ const PipelineWrapper: React.FC = ({
'This pipeline contains unsaved changes. To submit the pipeline the changes need to be saved.',
buttons: [
Dialog.cancelButton(),
- Dialog.okButton({ label: 'Save and Submit' }),
- ],
+ Dialog.okButton({ label: 'Save and Submit' })
+ ]
});
if (dialogResult.button && dialogResult.button.accept === true) {
await contextRef.current.save();
@@ -665,7 +722,7 @@ const PipelineWrapper: React.FC = ({
const pipelineName = PathExt.basename(
contextRef.current.path,
- PathExt.extname(contextRef.current.path),
+ PathExt.extname(contextRef.current.path)
);
// TODO: Parallelize this
@@ -676,20 +733,20 @@ const PipelineWrapper: React.FC = ({
return (
!runtime.metadata.runtime_enabled &&
!!runtimeTypes.find(
- (r: any) => runtime.metadata.runtime_type === r.id,
+ (r: any) => runtime.metadata.runtime_type === r.id
)
);
});
})
.catch((error) => RequestErrors.serverError(error));
const schema = await PipelineService.getRuntimesSchema().catch((error) =>
- RequestErrors.serverError(error),
+ RequestErrors.serverError(error)
);
const runtimeData = createRuntimeData({
schema,
runtimes,
- allowLocal: actionType === 'run',
+ allowLocal: actionType === 'run'
});
let title =
@@ -702,7 +759,7 @@ const PipelineWrapper: React.FC = ({
const res = await RequestErrors.noMetadataError(
'runtime',
`${actionType} pipeline.`,
- type !== undefined ? runtimeDisplayName : undefined,
+ type !== undefined ? runtimeDisplayName : undefined
);
if (res.button.label.includes(RUNTIMES_SCHEMASPACE)) {
@@ -724,16 +781,16 @@ const PipelineWrapper: React.FC = ({
return (
param.name !== '' &&
(node.app_data.component_parameters?.pipeline_parameters?.includes(
- param.name,
+ param.name
) ||
Object.values(node.app_data.component_parameters ?? {}).find(
(property: any) =>
property.widget === 'parameter' &&
- property.value === param.name,
+ property.value === param.name
))
);
});
- },
+ }
);
const parameters =
@@ -749,11 +806,11 @@ const PipelineWrapper: React.FC = ({
runtimeData={runtimeData}
pipelineType={type}
parameters={parameters}
- />,
+ />
),
buttons: [Dialog.cancelButton(), Dialog.okButton()],
defaultButton: 1,
- focusNodeSelector: '#pipeline_name',
+ focusNodeSelector: '#pipeline_name'
};
break;
case 'export':
@@ -766,11 +823,11 @@ const PipelineWrapper: React.FC = ({
pipelineType={type}
exportName={pipelineName}
parameters={parameters}
- />,
+ />
),
buttons: [Dialog.cancelButton(), Dialog.okButton()],
defaultButton: 1,
- focusNodeSelector: '#runtime_config',
+ focusNodeSelector: '#runtime_config'
};
break;
}
@@ -797,20 +854,20 @@ const PipelineWrapper: React.FC = ({
const configDetails = getConfigDetails(
runtimeData,
- dialogResult.value.runtime_config,
+ dialogResult.value.runtime_config
);
PipelineService.setNodePathsRelativeToWorkspace(
pipelineJson.pipelines[0],
getAllPaletteNodes(palette),
- contextRef.current.path,
+ contextRef.current.path
);
// Metadata
pipelineJson.pipelines[0].app_data.name =
dialogResult.value.pipeline_name ?? pipelineName;
pipelineJson.pipelines[0].app_data.source = PathExt.basename(
- contextRef.current.path,
+ contextRef.current.path
);
// Pipeline parameter overrides
@@ -851,7 +908,7 @@ const PipelineWrapper: React.FC = ({
case 'run':
PipelineService.submitPipeline(
pipelineJson,
- configDetails?.platform.displayName ?? '',
+ configDetails?.platform.displayName ?? ''
).catch((error) => RequestErrors.serverError(error));
break;
case 'export':
@@ -859,12 +916,12 @@ const PipelineWrapper: React.FC = ({
pipelineJson,
exportType,
exportPath,
- dialogResult.value.overwrite,
+ dialogResult.value.overwrite
).catch((error) => RequestErrors.serverError(error));
break;
}
},
- [context.model, palette, runtimeDisplayName, type, shell],
+ [context.model, palette, runtimeDisplayName, type, shell]
);
const handleClearPipeline = useCallback(async (data: any): Promise => {
@@ -874,8 +931,8 @@ const PipelineWrapper: React.FC = ({
buttons: [
Dialog.cancelButton(),
Dialog.okButton({ label: 'Clear All' }),
- Dialog.okButton({ label: 'Clear Canvas' }),
- ],
+ Dialog.okButton({ label: 'Clear Canvas' })
+ ]
}).then((result) => {
if (result.button.accept) {
const newPipeline: any = contextRef.current.model.toJSON();
@@ -892,7 +949,7 @@ const PipelineWrapper: React.FC = ({
// Remove all fields of pipeline properties except for the name/runtime (readonly)
newPipeline.pipelines[0].app_data.properties = {
name: pipelineProperties.name,
- runtime: pipelineProperties.runtime,
+ runtime: pipelineProperties.runtime
};
}
}
@@ -928,21 +985,21 @@ const PipelineWrapper: React.FC = ({
break;
case 'openComponentCatalogs':
shell.activateById(
- `elyra-metadata:${COMPONENT_CATALOGS_SCHEMASPACE}`,
+ `elyra-metadata:${COMPONENT_CATALOGS_SCHEMASPACE}`
);
break;
case 'openFile':
commands.execute(commandIDs.openDocManager, {
path: PipelineService.getWorkspaceRelativeNodePath(
contextRef.current.path,
- args.payload,
- ),
+ args.payload
+ )
});
break;
case 'openComponentDef':
handleOpenComponentDef(
args.payload.componentId,
- args.payload.componentSource,
+ args.payload.componentSource
);
break;
default:
@@ -955,8 +1012,8 @@ const PipelineWrapper: React.FC = ({
panelOpen,
shell,
commands,
- handleOpenComponentDef,
- ],
+ handleOpenComponentDef
+ ]
);
const toolbar = {
@@ -964,49 +1021,49 @@ const PipelineWrapper: React.FC = ({
{
action: 'run',
label: 'Run Pipeline',
- enable: true,
+ enable: true
},
{
action: 'save',
label: 'Save Pipeline',
enable: true,
iconEnabled: IconUtil.encode(savePipelineIcon),
- iconDisabled: IconUtil.encode(savePipelineIcon),
+ iconDisabled: IconUtil.encode(savePipelineIcon)
},
{
action: 'export',
label: 'Export Pipeline',
enable: true,
iconEnabled: IconUtil.encode(exportPipelineIcon),
- iconDisabled: IconUtil.encode(exportPipelineIcon),
+ iconDisabled: IconUtil.encode(exportPipelineIcon)
},
{
action: 'clear',
label: 'Clear Pipeline',
enable: true,
iconEnabled: IconUtil.encode(clearPipelineIcon),
- iconDisabled: IconUtil.encode(clearPipelineIcon),
+ iconDisabled: IconUtil.encode(clearPipelineIcon)
},
{
action: 'openRuntimes',
label: 'Open Runtimes',
enable: true,
iconEnabled: IconUtil.encode(runtimesIcon),
- iconDisabled: IconUtil.encode(runtimesIcon),
+ iconDisabled: IconUtil.encode(runtimesIcon)
},
{
action: 'openRuntimeImages',
label: 'Open Runtime Images',
enable: true,
iconEnabled: IconUtil.encode(containerIcon),
- iconDisabled: IconUtil.encode(containerIcon),
+ iconDisabled: IconUtil.encode(containerIcon)
},
{
action: 'openComponentCatalogs',
label: 'Open Component Catalogs',
enable: true,
iconEnabled: IconUtil.encode(componentCatalogIcon),
- iconDisabled: IconUtil.encode(componentCatalogIcon),
+ iconDisabled: IconUtil.encode(componentCatalogIcon)
},
{ action: 'undo', label: 'Undo' },
{ action: 'redo', label: 'Redo' },
@@ -1018,13 +1075,13 @@ const PipelineWrapper: React.FC = ({
{
action: 'arrangeHorizontally',
label: 'Arrange Horizontally',
- enable: true,
+ enable: true
},
{
action: 'arrangeVertically',
label: 'Arrange Vertically',
- enable: true,
- },
+ enable: true
+ }
],
rightBar: [
{
@@ -1032,7 +1089,7 @@ const PipelineWrapper: React.FC = ({
label: `Runtime: ${runtimeDisplayName}`,
incLabelWithIcon: 'before',
enable: false,
- kind: 'tertiary',
+ kind: 'tertiary'
// TODO: re-add icon
// iconEnabled: IconUtil.encode(ICON_MAP[type ?? ''] ?? pipelineIcon)
},
@@ -1040,9 +1097,9 @@ const PipelineWrapper: React.FC = ({
action: 'toggleOpenPanel',
label: panelOpen ? 'Close Panel' : 'Open Panel',
enable: true,
- iconTypeOverride: panelOpen ? 'paletteOpen' : 'paletteClose',
- },
- ],
+ iconTypeOverride: panelOpen ? 'paletteOpen' : 'paletteClose'
+ }
+ ]
};
const [defaultPosition, setDefaultPosition] = useState(10);
@@ -1064,7 +1121,7 @@ const PipelineWrapper: React.FC = ({
position = defaultPosition;
location = {
x: 75,
- y: 85,
+ y: 85
};
}
@@ -1073,18 +1130,18 @@ const PipelineWrapper: React.FC = ({
item.op = PipelineService.getNodeType(item.path);
item.path = PipelineService.getPipelineRelativeNodePath(
contextRef.current.path,
- item.path,
+ item.path
);
item.x = (location?.x ?? 0) + position;
item.y = (location?.y ?? 0) + position;
const success = ref.current?.addFile({
nodeTemplate: {
- op: item.op,
+ op: item.op
},
offsetX: item.x,
offsetY: item.y,
- path: item.path,
+ path: item.path
});
if (success) {
@@ -1105,13 +1162,13 @@ const PipelineWrapper: React.FC = ({
return showDialog({
title: 'Unsupported File(s)',
body: 'Only supported files (Notebooks, Python scripts, and R scripts) can be added to a pipeline.',
- buttons: [Dialog.okButton()],
+ buttons: [Dialog.okButton()]
});
}
return;
},
- [browserFactory, defaultPosition, shell, widgetId],
+ [browserFactory, defaultPosition, shell, widgetId]
);
const handleDrop = async (e: IDragEvent): Promise => {
@@ -1191,6 +1248,7 @@ export class PipelineEditorFactory extends ABCWidgetFactory {
addFileToPipelineSignal: Signal;
refreshPaletteSignal: Signal;
settings: ISettingRegistry.ISettings;
+ defaultRuntimeType: string;
constructor(options: any) {
super(options);
@@ -1200,6 +1258,7 @@ export class PipelineEditorFactory extends ABCWidgetFactory {
this.addFileToPipelineSignal = new Signal(this);
this.refreshPaletteSignal = new Signal(this);
this.settings = options.settings;
+ this.defaultRuntimeType = options.defaultRuntimeType;
}
protected createNewWidget(context: DocumentRegistry.Context): DocumentWidget {
@@ -1212,6 +1271,7 @@ export class PipelineEditorFactory extends ABCWidgetFactory {
addFileToPipelineSignal: this.addFileToPipelineSignal,
refreshPaletteSignal: this.refreshPaletteSignal,
settings: this.settings,
+ defaultRuntimeType: this.defaultRuntimeType
};
const content = new PipelineEditorWidget(props);
diff --git a/packages/pipeline-editor/src/PipelineExportDialog.tsx b/packages/pipeline-editor/src/PipelineExportDialog.tsx
index ca5ccddf4..1eac7faab 100644
--- a/packages/pipeline-editor/src/PipelineExportDialog.tsx
+++ b/packages/pipeline-editor/src/PipelineExportDialog.tsx
@@ -59,7 +59,7 @@ export const PipelineExportDialog: React.FC = ({
runtimeTypeInfo,
pipelineType,
exportName,
- parameters,
+ parameters
}) => {
return (