mirror of
https://github.com/langgenius/dify.git
synced 2026-07-21 02:28:30 +08:00
fix(trigger): surface webhook trigger quota exceeded as 429 response (#38656)
This commit is contained in:
parent
775ed70bac
commit
44fb074359
@ -7,6 +7,7 @@ from werkzeug.exceptions import NotFound, RequestEntityTooLarge
|
||||
from controllers.trigger import bp
|
||||
from core.trigger.debug.event_bus import TriggerDebugEventBus
|
||||
from core.trigger.debug.events import WebhookDebugEvent, build_webhook_pool_key
|
||||
from services.errors.app import QuotaExceededError
|
||||
from services.trigger.webhook_service import RawWebhookDataDict, WebhookService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -60,8 +61,15 @@ def handle_webhook(webhook_id: str):
|
||||
response_data, status_code = WebhookService.generate_webhook_response(node_config)
|
||||
return jsonify(response_data), status_code
|
||||
|
||||
except ValueError as e:
|
||||
raise NotFound(str(e))
|
||||
except QuotaExceededError:
|
||||
return jsonify(
|
||||
{
|
||||
"error": "Too Many Requests",
|
||||
"message": "Trigger event quota exceeded. Please upgrade your plan.",
|
||||
}
|
||||
), 429
|
||||
except ValueError as error:
|
||||
raise NotFound(str(error))
|
||||
except RequestEntityTooLarge:
|
||||
raise
|
||||
except Exception as e:
|
||||
|
||||
@ -101,6 +101,7 @@ class WebhookService:
|
||||
- Mapping[str, Any]: The node configuration data
|
||||
|
||||
Raises:
|
||||
QuotaExceededError: If the app trigger is rate limited
|
||||
ValueError: If webhook not found, app trigger not found, trigger disabled, or workflow not found
|
||||
"""
|
||||
with Session(db.engine) as session:
|
||||
@ -141,8 +142,10 @@ class WebhookService:
|
||||
# Only check enabled status if not in debug mode
|
||||
|
||||
if app_trigger.status == AppTriggerStatus.RATE_LIMITED:
|
||||
raise ValueError(
|
||||
f"Webhook trigger is rate limited for webhook {webhook_id}, please upgrade your plan."
|
||||
raise QuotaExceededError(
|
||||
feature=QuotaType.TRIGGER.value,
|
||||
tenant_id=webhook_trigger.tenant_id,
|
||||
required=1,
|
||||
)
|
||||
|
||||
if app_trigger.status != AppTriggerStatus.ENABLED:
|
||||
@ -799,6 +802,7 @@ class WebhookService:
|
||||
workflow: The workflow to execute
|
||||
|
||||
Raises:
|
||||
QuotaExceededError: If the tenant has exhausted its trigger quota
|
||||
ValueError: If tenant owner is not found
|
||||
Exception: If workflow execution fails
|
||||
"""
|
||||
|
||||
@ -201,9 +201,13 @@ class TestWebhookServiceLookupWithContainers:
|
||||
db_session_with_containers, app=app, node_id="node-1", status=AppTriggerStatus.RATE_LIMITED
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="rate limited"):
|
||||
with pytest.raises(QuotaExceededError) as exc_info:
|
||||
WebhookService.get_webhook_trigger_and_workflow(webhook_trigger.webhook_id)
|
||||
|
||||
assert exc_info.value.feature == QuotaType.TRIGGER.value
|
||||
assert exc_info.value.tenant_id == tenant.id
|
||||
assert exc_info.value.required == 1
|
||||
|
||||
def test_get_webhook_trigger_and_workflow_raises_when_app_trigger_disabled(
|
||||
self, db_session_with_containers: Session, flask_app_with_containers: Flask
|
||||
):
|
||||
|
||||
@ -5,6 +5,7 @@ import pytest
|
||||
from werkzeug.exceptions import NotFound, RequestEntityTooLarge
|
||||
|
||||
import controllers.trigger.webhook as module
|
||||
from services.errors.app import QuotaExceededError
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@ -83,6 +84,39 @@ class TestHandleWebhook:
|
||||
assert status == 400
|
||||
assert response["error"] == "Bad Request"
|
||||
|
||||
@patch.object(module.WebhookService, "get_webhook_trigger_and_workflow")
|
||||
@patch.object(module.WebhookService, "extract_and_validate_webhook_data")
|
||||
@patch.object(
|
||||
module.WebhookService,
|
||||
"trigger_workflow_execution",
|
||||
side_effect=QuotaExceededError(feature="trigger", tenant_id="tenant-1", required=1),
|
||||
)
|
||||
def test_quota_exceeded(self, mock_trigger, mock_extract, mock_get):
|
||||
mock_get.return_value = (DummyWebhookTrigger(), "workflow", "node_config")
|
||||
mock_extract.return_value = {"input": "x"}
|
||||
|
||||
response, status = module.handle_webhook("wh-1")
|
||||
|
||||
assert status == 429
|
||||
assert response == {
|
||||
"error": "Too Many Requests",
|
||||
"message": "Trigger event quota exceeded. Please upgrade your plan.",
|
||||
}
|
||||
|
||||
@patch.object(
|
||||
module.WebhookService,
|
||||
"get_webhook_trigger_and_workflow",
|
||||
side_effect=QuotaExceededError(feature="trigger", tenant_id="tenant-1", required=1),
|
||||
)
|
||||
def test_rate_limited(self, mock_get):
|
||||
response, status = module.handle_webhook("wh-1")
|
||||
|
||||
assert status == 429
|
||||
assert response == {
|
||||
"error": "Too Many Requests",
|
||||
"message": "Trigger event quota exceeded. Please upgrade your plan.",
|
||||
}
|
||||
|
||||
@patch.object(module.WebhookService, "get_webhook_trigger_and_workflow", side_effect=ValueError("missing"))
|
||||
def test_value_error_not_found(self, mock_get):
|
||||
with pytest.raises(NotFound):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user