Skip to content

refactor(http_request): add custom exception handling for HTTP request nodes #10219

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
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
18 changes: 18 additions & 0 deletions api/core/workflow/nodes/http_request/exc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class HttpRequestNodeError(ValueError):
"""Custom error for HTTP request node."""


class AuthorizationConfigError(HttpRequestNodeError):
"""Raised when authorization config is missing or invalid."""


class FileFetchError(HttpRequestNodeError):
"""Raised when a file cannot be fetched."""


class InvalidHttpMethodError(HttpRequestNodeError):
"""Raised when an invalid HTTP method is used."""


class ResponseSizeError(HttpRequestNodeError):
"""Raised when the response size exceeds the allowed threshold."""
20 changes: 13 additions & 7 deletions api/core/workflow/nodes/http_request/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
HttpRequestNodeTimeout,
Response,
)
from .exc import (
AuthorizationConfigError,
FileFetchError,
InvalidHttpMethodError,
ResponseSizeError,
)

BODY_TYPE_TO_CONTENT_TYPE = {
"json": "application/json",
Expand Down Expand Up @@ -51,7 +57,7 @@ def __init__(
# If authorization API key is present, convert the API key using the variable pool
if node_data.authorization.type == "api-key":
if node_data.authorization.config is None:
raise ValueError("authorization config is required")
raise AuthorizationConfigError("authorization config is required")
node_data.authorization.config.api_key = variable_pool.convert_template(
node_data.authorization.config.api_key
).text
Expand Down Expand Up @@ -116,7 +122,7 @@ def _init_body(self):
file_selector = data[0].file
file_variable = self.variable_pool.get_file(file_selector)
if file_variable is None:
raise ValueError(f"cannot fetch file with selector {file_selector}")
raise FileFetchError(f"cannot fetch file with selector {file_selector}")
file = file_variable.value
self.content = file_manager.download(file)
case "x-www-form-urlencoded":
Expand Down Expand Up @@ -155,12 +161,12 @@ def _assembling_headers(self) -> dict[str, Any]:
headers = deepcopy(self.headers) or {}
if self.auth.type == "api-key":
if self.auth.config is None:
raise ValueError("self.authorization config is required")
raise AuthorizationConfigError("self.authorization config is required")
if authorization.config is None:
raise ValueError("authorization config is required")
raise AuthorizationConfigError("authorization config is required")

if self.auth.config.api_key is None:
raise ValueError("api_key is required")
raise AuthorizationConfigError("api_key is required")

if not authorization.config.header:
authorization.config.header = "Authorization"
Expand All @@ -183,7 +189,7 @@ def _validate_and_parse_response(self, response: httpx.Response) -> Response:
else dify_config.HTTP_REQUEST_NODE_MAX_TEXT_SIZE
)
if executor_response.size > threshold_size:
raise ValueError(
raise ResponseSizeError(
f'{"File" if executor_response.is_file else "Text"} size is too large,'
f' max size is {threshold_size / 1024 / 1024:.2f} MB,'
f' but current size is {executor_response.readable_size}.'
Expand All @@ -196,7 +202,7 @@ def _do_http_request(self, headers: dict[str, Any]) -> httpx.Response:
do http request depending on api bundle
"""
if self.method not in {"get", "head", "post", "put", "delete", "patch"}:
raise ValueError(f"Invalid http method {self.method}")
raise InvalidHttpMethodError(f"Invalid http method {self.method}")

request_args = {
"url": self.url,
Expand Down
3 changes: 2 additions & 1 deletion api/core/workflow/nodes/http_request/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
HttpRequestNodeTimeout,
Response,
)
from .exc import HttpRequestNodeError

HTTP_REQUEST_DEFAULT_TIMEOUT = HttpRequestNodeTimeout(
connect=dify_config.HTTP_REQUEST_MAX_CONNECT_TIMEOUT,
Expand Down Expand Up @@ -77,7 +78,7 @@ def _run(self) -> NodeRunResult:
"request": http_executor.to_log(),
},
)
except Exception as e:
except HttpRequestNodeError as e:
logger.warning(f"http request node {self.node_id} failed to run: {e}")
return NodeRunResult(
status=WorkflowNodeExecutionStatus.FAILED,
Expand Down