Skip to content

fix: published workflow(tool) can be deleted. #17900

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions api/services/workflow_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,5 +515,10 @@ def delete_workflow(self, *, session: Session, workflow_id: str, tenant_id: str)
# Cannot delete a workflow that's currently in use by an app
raise WorkflowInUseError(f"Cannot delete workflow that is currently in use by app '{app.name}'")

# Check if this workflow is published as a tool
if workflow.tool_published:
# Cannot delete a workflow that's published as a tool
raise WorkflowInUseError("Cannot delete workflow that is published as a tool")

session.delete(workflow)
return True
117 changes: 117 additions & 0 deletions api/test_workflow_deletion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
from unittest.mock import MagicMock

import pytest
from sqlalchemy.orm import Session

from models.model import App
from models.workflow import Workflow
from services.workflow_service import DraftWorkflowDeletionError, WorkflowInUseError, WorkflowService


@pytest.fixture
def workflow_setup():
workflow_service = WorkflowService()
session = MagicMock(spec=Session)
tenant_id = "test-tenant-id"
workflow_id = "test-workflow-id"

# Mock workflow
workflow = MagicMock(spec=Workflow)
workflow.id = workflow_id
workflow.tenant_id = tenant_id
workflow.version = "1.0" # Not a draft
workflow.tool_published = False # Not published as a tool by default

# Mock app
app = MagicMock(spec=App)
app.id = "test-app-id"
app.name = "Test App"
app.workflow_id = None # Not used by an app by default

return {
"workflow_service": workflow_service,
"session": session,
"tenant_id": tenant_id,
"workflow_id": workflow_id,
"workflow": workflow,
"app": app,
}


def test_delete_workflow_success(workflow_setup):
# Setup mocks
workflow_setup["session"].scalar = MagicMock(
side_effect=[workflow_setup["workflow"], None]
) # Return workflow first, then None for app

# Call the method
result = workflow_setup["workflow_service"].delete_workflow(
session=workflow_setup["session"],
workflow_id=workflow_setup["workflow_id"],
tenant_id=workflow_setup["tenant_id"],
)

# Verify
assert result is True
workflow_setup["session"].delete.assert_called_once_with(workflow_setup["workflow"])


def test_delete_workflow_draft_error(workflow_setup):
# Setup mocks
workflow_setup["workflow"].version = "draft"
workflow_setup["session"].scalar = MagicMock(return_value=workflow_setup["workflow"])

# Call the method and verify exception
with pytest.raises(DraftWorkflowDeletionError):
workflow_setup["workflow_service"].delete_workflow(
session=workflow_setup["session"],
workflow_id=workflow_setup["workflow_id"],
tenant_id=workflow_setup["tenant_id"],
)

# Verify
workflow_setup["session"].delete.assert_not_called()


def test_delete_workflow_in_use_by_app_error(workflow_setup):
# Setup mocks
workflow_setup["app"].workflow_id = workflow_setup["workflow_id"]
workflow_setup["session"].scalar = MagicMock(
side_effect=[workflow_setup["workflow"], workflow_setup["app"]]
) # Return workflow first, then app

# Call the method and verify exception
with pytest.raises(WorkflowInUseError) as excinfo:
workflow_setup["workflow_service"].delete_workflow(
session=workflow_setup["session"],
workflow_id=workflow_setup["workflow_id"],
tenant_id=workflow_setup["tenant_id"],
)

# Verify error message contains app name
assert "Cannot delete workflow that is currently in use by app" in str(excinfo.value)

# Verify
workflow_setup["session"].delete.assert_not_called()


def test_delete_workflow_published_as_tool_error(workflow_setup):
# Setup mocks
workflow_setup["workflow"].tool_published = True
workflow_setup["session"].scalar = MagicMock(
side_effect=[workflow_setup["workflow"], None]
) # Return workflow first, then None for app

# Call the method and verify exception
with pytest.raises(WorkflowInUseError) as excinfo:
workflow_setup["workflow_service"].delete_workflow(
session=workflow_setup["session"],
workflow_id=workflow_setup["workflow_id"],
tenant_id=workflow_setup["tenant_id"],
)

# Verify error message
assert "Cannot delete workflow that is published as a tool" in str(excinfo.value)

# Verify
workflow_setup["session"].delete.assert_not_called()