diff --git a/backend/workflow_manager/workflow_v2/workflow_helper.py b/backend/workflow_manager/workflow_v2/workflow_helper.py index 73aef3e9db..759547a87c 100644 --- a/backend/workflow_manager/workflow_v2/workflow_helper.py +++ b/backend/workflow_manager/workflow_v2/workflow_helper.py @@ -1000,10 +1000,24 @@ def can_update_workflow(workflow_id: str) -> dict[str, Any]: workflow: Workflow = Workflow.objects.get(pk=workflow_id) if not workflow or workflow is None: raise WorkflowDoesNotExistError() - used_count = Pipeline.objects.filter(workflow=workflow).count() - if used_count == 0: - used_count = APIDeployment.objects.filter(workflow=workflow).count() - return {"can_update": used_count == 0} + + pipeline_names = list( + Pipeline.objects.filter(workflow=workflow).values_list( + "pipeline_name", flat=True + ) + ) + api_names = list( + APIDeployment.objects.filter(workflow=workflow).values_list( + "display_name", flat=True + ) + ) + total_usage = len(pipeline_names) + len(api_names) + + return { + "can_update": total_usage == 0, + "pipeline_names": pipeline_names, + "api_names": api_names, + } except Workflow.DoesNotExist: logger.error(f"Error getting workflow: {id}") raise WorkflowDoesNotExistError() diff --git a/frontend/src/components/workflows/workflow/Workflows.jsx b/frontend/src/components/workflows/workflow/Workflows.jsx index 9b6ee23989..e7c493cf09 100644 --- a/frontend/src/components/workflows/workflow/Workflows.jsx +++ b/frontend/src/components/workflows/workflow/Workflows.jsx @@ -146,37 +146,60 @@ function Workflows() { }); } - const canDeleteProject = async (id) => { - let status = false; - await projectApiService.canUpdate(id).then((res) => { - status = res?.data?.can_update || false; - }); - return status; + const checkWorkflowUsage = async (id) => { + const res = await projectApiService.canUpdate(id); + return { + can_update: res?.data?.can_update || false, + pipeline_names: res?.data?.pipeline_names || [], + api_names: res?.data?.api_names || [], + }; + }; + + const getUsageMessage = (workflowName, pipelineNames, apiNames) => { + const allNames = [...apiNames, ...pipelineNames]; + const total = allNames.length; + if (total === 0) return ""; + const firstName = `"${allNames[0]}"`; + if (total === 1) { + return `Cannot delete "${workflowName}" as it is used in ${firstName}.`; + } + const remaining = total - 1; + const pipelineLabel = remaining === 1 ? "pipeline" : "pipelines"; + return `Cannot delete "${workflowName}" as it is used in ${firstName} and ${remaining} other API/ETL/Task ${pipelineLabel}.`; }; const deleteProject = async (_evt, project) => { - const canDelete = await canDeleteProject(project.id); - if (canDelete) { - projectApiService - .deleteProject(project.id) - .then(() => { - getProjectList(); - setAlertDetails({ - type: "success", - content: "Workflow deleted successfully", + try { + const usage = await checkWorkflowUsage(project.id); + if (usage.can_update) { + projectApiService + .deleteProject(project.id) + .then(() => { + getProjectList(); + setAlertDetails({ + type: "success", + content: "Workflow deleted successfully", + }); + }) + .catch((err) => { + setAlertDetails( + handleException(err, `Unable to delete workflow ${project.id}`) + ); }); - }) - .catch((err) => { - setAlertDetails( - handleException(err, `Unable to delete workflow ${project.id}`) - ); + } else { + setAlertDetails({ + type: "error", + content: getUsageMessage( + project.workflow_name, + usage.pipeline_names, + usage.api_names + ), }); - } else { - setAlertDetails({ - type: "error", - content: - "Cannot delete this Workflow, since it is used in one or many of the API/ETL/Task pipelines", - }); + } + } catch (err) { + setAlertDetails( + handleException(err, `Unable to delete workflow ${project.id}`) + ); } };