diff --git a/packages/pipeline-editor/src/PipelineEditorWidget.tsx b/packages/pipeline-editor/src/PipelineEditorWidget.tsx index f93184ab1..1b6db88b6 100644 --- a/packages/pipeline-editor/src/PipelineEditorWidget.tsx +++ b/packages/pipeline-editor/src/PipelineEditorWidget.tsx @@ -58,7 +58,13 @@ 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'; @@ -246,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); + } = usePalette(type, filePersistedRuntimeImages); useEffect(() => { const handleMutateSignal = (): void => { diff --git a/packages/pipeline-editor/src/pipeline-hooks.ts b/packages/pipeline-editor/src/pipeline-hooks.ts index 7eb6b06ae..e72196e4e 100644 --- a/packages/pipeline-editor/src/pipeline-hooks.ts +++ b/packages/pipeline-editor/src/pipeline-hooks.ts @@ -44,15 +44,58 @@ const metadataFetcher = async (key: string): Promise => { return await MetadataService.getMetadata(key); }; -export const useRuntimeImages = (): IReturn => { +export const useRuntimeImages = ( + additionalRuntimeImages?: IRuntimeImage[] +): IReturn => { const { data, error } = useSWR( 'runtime-images', metadataFetcher ); - data?.sort((a, b) => 0 - (a.name > b.name ? -1 : 1)); + let result: IRuntimeImage[] | undefined = data; + + if (result && additionalRuntimeImages) { + // Sort and remove duplicates from additionalRuntimeImages + additionalRuntimeImages.sort((a, b) => 0 - (a.name > b.name ? -1 : 1)); + additionalRuntimeImages = additionalRuntimeImages.filter( + (image, index, self) => + index === + self.findIndex( + (otherImage) => + image.name === otherImage.name && + image.display_name === otherImage.display_name && + image.metadata.image_name === otherImage.metadata.image_name + ) + ); - return { data, error }; + // Remove previously added additionalRuntimeImages from result + result = result.filter( + (runtimeImage) => + !additionalRuntimeImages || + additionalRuntimeImages.findIndex( + (additionalRuntimeImage) => + runtimeImage.name === additionalRuntimeImage.name && + runtimeImage.display_name === additionalRuntimeImage.display_name && + runtimeImage.metadata.image_name === + additionalRuntimeImage.metadata.image_name + ) < 0 + ); + + // Find out which additionalRuntimeImages are not yet in result + const existingImageNames = result.map( + (runtimeImage) => runtimeImage.metadata.image_name + ); + const runtimeImagesToAdd = additionalRuntimeImages.filter( + (additionalRuntimeImage) => + !existingImageNames.includes(additionalRuntimeImage.metadata.image_name) + ); + + // Sort and add missing images to result (at the end) + result.sort((a, b) => 0 - (a.name > b.name ? -1 : 1)); + Array.prototype.push.apply(result, runtimeImagesToAdd); + } + + return { data: result, error }; }; const schemaFetcher = async (key: string): Promise => { @@ -267,8 +310,13 @@ const updateRuntimeImages = ( } }; -export const usePalette = (type = 'local'): IReturn => { - const { data: runtimeImages, error: runtimeError } = useRuntimeImages(); +export const usePalette = ( + type = 'local', + additionalRuntimeImages?: IRuntimeImage[] +): IReturn => { + const { data: runtimeImages, error: runtimeError } = useRuntimeImages( + additionalRuntimeImages + ); const { data: palette,