diff --git a/src/components/catalog/apis/artifactHub.ts b/src/components/catalog/apis/artifactHub.ts index 3ba9d047..c78f3085 100644 --- a/src/components/catalog/apis/artifactHub.ts +++ b/src/components/catalog/apis/artifactHub.ts @@ -102,6 +102,7 @@ export const createArtifactHubTask = ( namespace: string, version: string, isDevConsoleProxyAvailable?: boolean, + customName?: string, ) => { const fetchTask = async (): Promise => { if (isDevConsoleProxyAvailable) { @@ -122,6 +123,9 @@ export const createArtifactHubTask = ( return fetchTask() .then((task: K8sResourceKind) => { task.metadata.namespace = namespace; + if (customName) { + task.metadata.name = customName; + } task.metadata.annotations = { ...task.metadata.annotations, [TektonTaskAnnotation.installedFrom]: ARTIFACTHUB, @@ -185,6 +189,7 @@ export const updateArtifactHubTask = async ( export const fetchArtifactHubTasks = async ( query: string, + // eslint-disable-next-line @typescript-eslint/no-inferrable-types limit: number = 20, ): Promise => { try { diff --git a/src/components/pipeline-builder/PipelineBuilderForm.tsx b/src/components/pipeline-builder/PipelineBuilderForm.tsx index 3cdf0a24..9d758a5d 100644 --- a/src/components/pipeline-builder/PipelineBuilderForm.tsx +++ b/src/components/pipeline-builder/PipelineBuilderForm.tsx @@ -105,7 +105,6 @@ const PipelineBuilderForm: React.FC = (props) => { const updateTasks = (changes: CleanupResults): void => { const { tasks, listTasks, finallyTasks, finallyListTasks, loadingTasks } = changes; - setFieldValue('formData', { ...formData, tasks, @@ -179,6 +178,20 @@ const PipelineBuilderForm: React.FC = (props) => { } }, [selectedTask]); + const handleRemoveTask = React.useCallback( + async (taskName: string) => { + setSelectedTask(null); + updateTasks( + applyChange( + taskGroup, + { type: UpdateOperationType.REMOVE_TASK, data: { taskName } }, + namespace, + ), + ); + }, + [namespace, taskGroup, updateTasks], + ); + return ( = (props) => { onRemoveTask={(taskName: string) => { launchModal(RemoveTaskModal, { taskName, - onRemove: () => { - setSelectedTask(null); - updateTasks( - applyChange( - taskGroup, - { - type: UpdateOperationType.REMOVE_TASK, - data: { taskName }, - }, - namespace, - ), - ); - }, + onRemove: () => handleRemoveTask(taskName), }); }} selectedData={selectedTask} diff --git a/src/components/quick-search/utils/quick-search-utils.tsx b/src/components/quick-search/utils/quick-search-utils.tsx index 9a3c35c7..c2fb5702 100644 --- a/src/components/quick-search/utils/quick-search-utils.tsx +++ b/src/components/quick-search/utils/quick-search-utils.tsx @@ -20,7 +20,7 @@ export const handleCta = async ( closeModal(); await callback({ ...callbackProps, - selectedVersion: item.data?.version, + selectedVersion: item.data?.version ?? item.data?.task?.version, selectedItem: item, }); removeQueryArgument('catalogSearch'); diff --git a/src/components/task-quicksearch/PipelineQuickSearch.tsx b/src/components/task-quicksearch/PipelineQuickSearch.tsx index 39118878..a8d56e99 100644 --- a/src/components/task-quicksearch/PipelineQuickSearch.tsx +++ b/src/components/task-quicksearch/PipelineQuickSearch.tsx @@ -20,6 +20,7 @@ import { TaskProviders, updateTask, } from './pipeline-quicksearch-utils'; +import { safeName } from '../pipeline-builder/utils'; import PipelineQuickSearchDetails from './PipelineQuickSearchDetails'; import { CatalogServiceProvider } from '../catalog/service'; import { CatalogService } from '../catalog/types'; @@ -64,6 +65,51 @@ const Contents: React.FC< useLoadingTaskCleanup(onUpdateTasks, taskGroup); useCleanupOnFailure(failedTasks, onUpdateTasks, taskGroup); + // Get all existing task names from taskGroup and installed tasks + const getExistingTaskNames = (): string[] => { + const taskNames = new Set(); + [ + ...taskGroup.tasks, + ...taskGroup.finallyTasks, + ...taskGroup.listTasks, + ...taskGroup.loadingTasks, + ...taskGroup.finallyListTasks, + ].forEach((t) => { + if (t?.name) taskNames.add(t.name); + }); + + // Add installed catalog items (avoid duplicates) + catalogService.items.forEach((catalogItem) => { + const name = catalogItem.data?.metadata?.name; + if (name) taskNames.add(name); + }); + return Array.from(taskNames); + }; + + const handleTaskCreationWithNameConflict = ( + taskName: string, + createTaskFn: (taskNameToUse?: string) => Promise, + resolve: (value: any) => void, + ) => { + // Checking if task with same name already exists, if yes then create with a different name to avoid conflict + const existingTaskNames = getExistingTaskNames(); + if (existingTaskNames.includes(taskName)) { + const taskNameToUse = safeName(existingTaskNames, taskName); + createTaskFn(taskNameToUse) + .then(() => + resolve( + savedCallback.current({ + metadata: { name: taskNameToUse }, + }), + ), + ) + .catch(() => setFailedTasks([...failedTasks, taskNameToUse])); + } else { + resolve(savedCallback.current({ metadata: { name: taskName } })); + createTaskFn().catch(() => setFailedTasks([...failedTasks, taskName])); + } + }; + const catalogServiceItems = catalogService.items.reduce((acc, item) => { const installedTask = findInstalledTask(catalogService.items, item); @@ -102,11 +148,11 @@ const Contents: React.FC< ).catch(() => setFailedTasks([...failedTasks, item.data.name])); } } else { - resolve( - savedCallback.current({ metadata: { name: item.data.name } }), - ); - createTask(selectedVersionUrl, namespace).catch(() => - setFailedTasks([...failedTasks, item.data.name]), + handleTaskCreationWithNameConflict( + item.data.name, + (taskNameToUse) => + createTask(selectedVersionUrl, namespace, taskNameToUse), + resolve, ); } } else { @@ -143,18 +189,17 @@ const Contents: React.FC< ); } } else { - resolve( - savedCallback.current({ - metadata: { name: item.data.task.name }, - }), - ); - createArtifactHubTask( - selectedVersionUrl, - namespace, - selectedVersion, - isDevConsoleProxyAvailable, - ).catch(() => - setFailedTasks([...failedTasks, item.data.task.name]), + handleTaskCreationWithNameConflict( + item.data.task.name, + (taskNameToUse) => + createArtifactHubTask( + selectedVersionUrl, + namespace, + selectedVersion, + isDevConsoleProxyAvailable, + taskNameToUse, + ), + resolve, ); } } diff --git a/src/components/task-quicksearch/pipeline-quicksearch-utils.ts b/src/components/task-quicksearch/pipeline-quicksearch-utils.ts index 0a28b25c..6cddd0da 100644 --- a/src/components/task-quicksearch/pipeline-quicksearch-utils.ts +++ b/src/components/task-quicksearch/pipeline-quicksearch-utils.ts @@ -174,12 +174,19 @@ export const updateTask = async ( }); }; -export const createTask = (url: string, namespace: string) => { +export const createTask = ( + url: string, + namespace: string, + customName?: string, +) => { return consoleFetch(url) .then(async (res) => { const yaml = await res.text(); const task = load(yaml) as TaskKind; task.metadata.namespace = namespace; + if (customName) { + task.metadata.name = customName; + } task.metadata.annotations = { ...task.metadata.annotations, [TektonTaskAnnotation.installedFrom]: TEKTONHUB,