feat(api): add delete workflow functionality with error handling (#33657)

This commit is contained in:
GuanMu
2026-03-30 14:56:04 +08:00
committed by GitHub
parent 7dd802201f
commit 8a277da278
3 changed files with 86 additions and 2 deletions

View File

@@ -53,6 +53,7 @@ from services.rag_pipeline.pipeline_generate_service import PipelineGenerateServ
from services.rag_pipeline.rag_pipeline import RagPipelineService
from services.rag_pipeline.rag_pipeline_manage_service import RagPipelineManageService
from services.rag_pipeline.rag_pipeline_transform_service import RagPipelineTransformService
from services.workflow_service import DraftWorkflowDeletionError, WorkflowInUseError, WorkflowService
logger = logging.getLogger(__name__)
@@ -781,7 +782,38 @@ class RagPipelineByIdApi(Resource):
# Commit the transaction in the controller
session.commit()
return workflow
return workflow
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@get_rag_pipeline
def delete(self, pipeline: Pipeline, workflow_id: str):
"""
Delete a published workflow version that is not currently active on the pipeline.
"""
if pipeline.workflow_id == workflow_id:
abort(400, description=f"Cannot delete workflow that is currently in use by pipeline '{pipeline.id}'")
workflow_service = WorkflowService()
with Session(db.engine) as session:
try:
workflow_service.delete_workflow(
session=session,
workflow_id=workflow_id,
tenant_id=pipeline.tenant_id,
)
session.commit()
except WorkflowInUseError as e:
abort(400, description=str(e))
except DraftWorkflowDeletionError as e:
abort(400, description=str(e))
except ValueError as e:
raise NotFound(str(e))
return None, 204
@console_ns.route("/rag/pipelines/<uuid:pipeline_id>/workflows/published/processing/parameters")

View File

@@ -32,6 +32,7 @@ from extensions.ext_database import db
# Configure logging for test containers
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)
_TEST_SANDBOX_IMAGE = os.getenv("TEST_SANDBOX_IMAGE", "langgenius/dify-sandbox:0.2.12")
DEFAULT_SANDBOX_TEST_IMAGE = "langgenius/dify-sandbox:0.2.14"
SANDBOX_TEST_IMAGE_ENV = "DIFY_SANDBOX_TEST_IMAGE"

View File

@@ -2,7 +2,7 @@ from datetime import datetime
from unittest.mock import MagicMock, patch
import pytest
from werkzeug.exceptions import Forbidden, HTTPException, NotFound
from werkzeug.exceptions import BadRequest, Forbidden, HTTPException, NotFound
import services
from controllers.console import console_ns
@@ -692,6 +692,57 @@ class TestRagPipelineByIdApi:
result, status = method(api, pipeline, "w1")
assert status == 400
def test_delete_success(self, app):
api = RagPipelineByIdApi()
method = unwrap(api.delete)
pipeline = MagicMock(tenant_id="t1", workflow_id="active-workflow", id="pipeline-1")
workflow_service = MagicMock()
session = MagicMock()
session_ctx = MagicMock()
session_ctx.__enter__.return_value = session
session_ctx.__exit__.return_value = None
fake_db = MagicMock()
fake_db.engine = MagicMock()
with (
app.test_request_context("/", method="DELETE"),
patch(
"controllers.console.datasets.rag_pipeline.rag_pipeline_workflow.db",
fake_db,
),
patch(
"controllers.console.datasets.rag_pipeline.rag_pipeline_workflow.Session",
return_value=session_ctx,
),
patch(
"controllers.console.datasets.rag_pipeline.rag_pipeline_workflow.WorkflowService",
return_value=workflow_service,
),
):
result = method(api, pipeline, "old-workflow")
workflow_service.delete_workflow.assert_called_once_with(
session=session,
workflow_id="old-workflow",
tenant_id="t1",
)
session.commit.assert_called_once()
assert result == (None, 204)
def test_delete_active_workflow_rejected(self, app):
api = RagPipelineByIdApi()
method = unwrap(api.delete)
pipeline = MagicMock(tenant_id="t1", workflow_id="active-workflow", id="pipeline-1")
with app.test_request_context("/", method="DELETE"):
with pytest.raises(BadRequest, match="currently in use by pipeline"):
method(api, pipeline, "active-workflow")
class TestRagPipelineWorkflowLastRunApi:
def test_last_run_success(self, app):