mirror of
https://github.com/langgenius/dify.git
synced 2026-04-22 07:46:31 +08:00
chore: validate param type of application/json when call a webhook (#25074)
This commit is contained in:
parent
7120c6414c
commit
2013ceb9d2
@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from flask import jsonify
|
from flask import jsonify
|
||||||
from werkzeug.exceptions import BadRequest, NotFound
|
from werkzeug.exceptions import NotFound
|
||||||
|
|
||||||
from controllers.trigger import bp
|
from controllers.trigger import bp
|
||||||
from services.webhook_service import WebhookService
|
from services.webhook_service import WebhookService
|
||||||
@ -28,7 +28,7 @@ def handle_webhook(webhook_id: str):
|
|||||||
# Validate request against node configuration
|
# Validate request against node configuration
|
||||||
validation_result = WebhookService.validate_webhook_request(webhook_data, node_config)
|
validation_result = WebhookService.validate_webhook_request(webhook_data, node_config)
|
||||||
if not validation_result["valid"]:
|
if not validation_result["valid"]:
|
||||||
raise BadRequest(validation_result["error"])
|
return jsonify({"error": "Bad Request", "message": validation_result["error"]}), 400
|
||||||
|
|
||||||
# Process webhook call (send to Celery)
|
# Process webhook call (send to Celery)
|
||||||
WebhookService.trigger_workflow_execution(webhook_trigger, webhook_data, workflow)
|
WebhookService.trigger_workflow_execution(webhook_trigger, webhook_data, workflow)
|
||||||
|
|||||||
@ -35,7 +35,17 @@ class WebhookBodyParameter(BaseModel):
|
|||||||
"""Body parameter with type information."""
|
"""Body parameter with type information."""
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
type: Literal["string", "number", "boolean", "object", "array", "file"] = "string"
|
type: Literal[
|
||||||
|
"string",
|
||||||
|
"number",
|
||||||
|
"boolean",
|
||||||
|
"object",
|
||||||
|
"array[string]",
|
||||||
|
"array[number]",
|
||||||
|
"array[boolean]",
|
||||||
|
"array[object]",
|
||||||
|
"file",
|
||||||
|
] = "string"
|
||||||
required: bool = False
|
required: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ from sqlalchemy.orm import Session
|
|||||||
|
|
||||||
from core.file.models import FileTransferMethod
|
from core.file.models import FileTransferMethod
|
||||||
from core.tools.tool_file_manager import ToolFileManager
|
from core.tools.tool_file_manager import ToolFileManager
|
||||||
|
from core.variables.types import SegmentType
|
||||||
from extensions.ext_database import db
|
from extensions.ext_database import db
|
||||||
from factories import file_factory
|
from factories import file_factory
|
||||||
from models.account import Account, TenantAccountJoin, TenantAccountRole
|
from models.account import Account, TenantAccountJoin, TenantAccountRole
|
||||||
@ -186,7 +187,7 @@ class WebhookService:
|
|||||||
param_name = param.get("name", "")
|
param_name = param.get("name", "")
|
||||||
if param_name not in webhook_data["query_params"]:
|
if param_name not in webhook_data["query_params"]:
|
||||||
return {"valid": False, "error": f"Required query parameter missing: {param_name}"}
|
return {"valid": False, "error": f"Required query parameter missing: {param_name}"}
|
||||||
|
|
||||||
if configured_content_type == "text/plain":
|
if configured_content_type == "text/plain":
|
||||||
# For text/plain, just validate that we have a body if any body params are configured as required
|
# For text/plain, just validate that we have a body if any body params are configured as required
|
||||||
body_params = node_data.get("body", [])
|
body_params = node_data.get("body", [])
|
||||||
@ -195,21 +196,52 @@ class WebhookService:
|
|||||||
raw_content = body_data.get("raw", "")
|
raw_content = body_data.get("raw", "")
|
||||||
if not raw_content or not isinstance(raw_content, str):
|
if not raw_content or not isinstance(raw_content, str):
|
||||||
return {"valid": False, "error": "Required body content missing for text/plain request"}
|
return {"valid": False, "error": "Required body content missing for text/plain request"}
|
||||||
|
|
||||||
|
elif configured_content_type == "application/json":
|
||||||
|
# For application/json, validate both existence and types of parameters
|
||||||
|
body_params = node_data.get("body", [])
|
||||||
|
body_data = webhook_data.get("body", {})
|
||||||
|
|
||||||
|
for body_param in body_params:
|
||||||
|
param_name = body_param.get("name", "")
|
||||||
|
param_type = body_param.get("type", SegmentType.STRING)
|
||||||
|
is_required = body_param.get("required", False)
|
||||||
|
|
||||||
|
# Handle regular JSON parameters
|
||||||
|
param_exists = param_name in body_data
|
||||||
|
|
||||||
|
# Check if required parameter exists
|
||||||
|
if is_required and not param_exists:
|
||||||
|
return {"valid": False, "error": f"Required body parameter missing: {param_name}"}
|
||||||
|
|
||||||
|
# Validate parameter type if it exists
|
||||||
|
if param_exists:
|
||||||
|
param_value = body_data[param_name]
|
||||||
|
validation_result = cls._validate_json_parameter_type(param_name, param_value, param_type)
|
||||||
|
if not validation_result["valid"]:
|
||||||
|
return validation_result
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
# For other content types (multipart/form-data, application/x-www-form-urlencoded, etc.)
|
||||||
|
# Only validate existence of required parameters, no type validation
|
||||||
body_params = node_data.get("body", [])
|
body_params = node_data.get("body", [])
|
||||||
for body_param in body_params:
|
for body_param in body_params:
|
||||||
if body_param.get("required", False):
|
param_name = body_param.get("name", "")
|
||||||
param_name = body_param.get("name", "")
|
param_type = body_param.get("type", SegmentType.STRING)
|
||||||
param_type = body_param.get("type", "string")
|
is_required = body_param.get("required", False)
|
||||||
|
|
||||||
# Check if parameter exists
|
if not is_required:
|
||||||
if param_type == "file":
|
continue
|
||||||
file_obj = webhook_data.get("files", {}).get(param_name)
|
|
||||||
if not file_obj:
|
# Check if parameter exists
|
||||||
return {"valid": False, "error": f"Required file parameter missing: {param_name}"}
|
if param_type == SegmentType.FILE:
|
||||||
else:
|
file_obj = webhook_data.get("files", {}).get(param_name)
|
||||||
if param_name not in webhook_data.get("body", {}):
|
if not file_obj:
|
||||||
return {"valid": False, "error": f"Required body parameter missing: {param_name}"}
|
return {"valid": False, "error": f"Required file parameter missing: {param_name}"}
|
||||||
|
else:
|
||||||
|
body_data = webhook_data.get("body", {})
|
||||||
|
if param_name not in body_data:
|
||||||
|
return {"valid": False, "error": f"Required body parameter missing: {param_name}"}
|
||||||
|
|
||||||
return {"valid": True}
|
return {"valid": True}
|
||||||
|
|
||||||
@ -217,6 +249,84 @@ class WebhookService:
|
|||||||
logger.exception("Validation error")
|
logger.exception("Validation error")
|
||||||
return {"valid": False, "error": "Validation failed"}
|
return {"valid": False, "error": "Validation failed"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _validate_json_parameter_type(cls, param_name: str, param_value: Any, param_type: str) -> dict[str, Any]:
|
||||||
|
"""Validate JSON parameter type against expected type."""
|
||||||
|
try:
|
||||||
|
if param_type == SegmentType.STRING:
|
||||||
|
if not isinstance(param_value, str):
|
||||||
|
return {
|
||||||
|
"valid": False,
|
||||||
|
"error": f"Parameter '{param_name}' must be a string, got {type(param_value).__name__}",
|
||||||
|
}
|
||||||
|
|
||||||
|
elif param_type == SegmentType.NUMBER:
|
||||||
|
if not isinstance(param_value, (int, float)):
|
||||||
|
return {
|
||||||
|
"valid": False,
|
||||||
|
"error": f"Parameter '{param_name}' must be a number, got {type(param_value).__name__}",
|
||||||
|
}
|
||||||
|
|
||||||
|
elif param_type == SegmentType.BOOLEAN:
|
||||||
|
if not isinstance(param_value, bool):
|
||||||
|
return {
|
||||||
|
"valid": False,
|
||||||
|
"error": f"Parameter '{param_name}' must be a boolean, got {type(param_value).__name__}",
|
||||||
|
}
|
||||||
|
|
||||||
|
elif param_type == SegmentType.OBJECT:
|
||||||
|
if not isinstance(param_value, dict):
|
||||||
|
return {
|
||||||
|
"valid": False,
|
||||||
|
"error": f"Parameter '{param_name}' must be an object, got {type(param_value).__name__}",
|
||||||
|
}
|
||||||
|
|
||||||
|
elif param_type == SegmentType.ARRAY_STRING:
|
||||||
|
if not isinstance(param_value, list):
|
||||||
|
return {
|
||||||
|
"valid": False,
|
||||||
|
"error": f"Parameter '{param_name}' must be an array, got {type(param_value).__name__}",
|
||||||
|
}
|
||||||
|
if not all(isinstance(item, str) for item in param_value):
|
||||||
|
return {"valid": False, "error": f"Parameter '{param_name}' must be an array of strings"}
|
||||||
|
|
||||||
|
elif param_type == SegmentType.ARRAY_NUMBER:
|
||||||
|
if not isinstance(param_value, list):
|
||||||
|
return {
|
||||||
|
"valid": False,
|
||||||
|
"error": f"Parameter '{param_name}' must be an array, got {type(param_value).__name__}",
|
||||||
|
}
|
||||||
|
if not all(isinstance(item, (int, float)) for item in param_value):
|
||||||
|
return {"valid": False, "error": f"Parameter '{param_name}' must be an array of numbers"}
|
||||||
|
|
||||||
|
elif param_type == SegmentType.ARRAY_BOOLEAN:
|
||||||
|
if not isinstance(param_value, list):
|
||||||
|
return {
|
||||||
|
"valid": False,
|
||||||
|
"error": f"Parameter '{param_name}' must be an array, got {type(param_value).__name__}",
|
||||||
|
}
|
||||||
|
if not all(isinstance(item, bool) for item in param_value):
|
||||||
|
return {"valid": False, "error": f"Parameter '{param_name}' must be an array of booleans"}
|
||||||
|
|
||||||
|
elif param_type == SegmentType.ARRAY_OBJECT:
|
||||||
|
if not isinstance(param_value, list):
|
||||||
|
return {
|
||||||
|
"valid": False,
|
||||||
|
"error": f"Parameter '{param_name}' must be an array, got {type(param_value).__name__}",
|
||||||
|
}
|
||||||
|
if not all(isinstance(item, dict) for item in param_value):
|
||||||
|
return {"valid": False, "error": f"Parameter '{param_name}' must be an array of objects"}
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Unknown type, skip validation
|
||||||
|
logger.warning("Unknown parameter type: %s for parameter %s", param_type, param_name)
|
||||||
|
|
||||||
|
return {"valid": True}
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Type validation error for parameter %s", param_name)
|
||||||
|
return {"valid": False, "error": f"Type validation failed for parameter '{param_name}'"}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def trigger_workflow_execution(
|
def trigger_workflow_execution(
|
||||||
cls, webhook_trigger: WorkflowWebhookTrigger, webhook_data: dict[str, Any], workflow: Workflow
|
cls, webhook_trigger: WorkflowWebhookTrigger, webhook_data: dict[str, Any], workflow: Workflow
|
||||||
|
|||||||
@ -202,24 +202,24 @@ class TestWebhookServiceUnit:
|
|||||||
result = WebhookService.validate_webhook_request(webhook_data, node_config)
|
result = WebhookService.validate_webhook_request(webhook_data, node_config)
|
||||||
|
|
||||||
assert result["valid"] is False
|
assert result["valid"] is False
|
||||||
assert "Required file parameter missing: upload" in result["error"]
|
assert "Required body parameter missing: upload" in result["error"]
|
||||||
|
|
||||||
def test_validate_webhook_request_text_plain_with_required_body(self):
|
def test_validate_webhook_request_text_plain_with_required_body(self):
|
||||||
"""Test webhook validation for text/plain content type with required body content."""
|
"""Test webhook validation for text/plain content type with required body content."""
|
||||||
# Test case 1: text/plain with raw content - should pass
|
# Test case 1: text/plain with raw content - should pass
|
||||||
webhook_data = {
|
webhook_data = {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"headers": {"content-type": "text/plain"},
|
"headers": {"content-type": "text/plain"},
|
||||||
"query_params": {},
|
"query_params": {},
|
||||||
"body": {"raw": "Hello World"},
|
"body": {"raw": "Hello World"},
|
||||||
"files": {}
|
"files": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
node_config = {
|
node_config = {
|
||||||
"data": {
|
"data": {
|
||||||
"method": "post",
|
"method": "post",
|
||||||
"content_type": "text/plain",
|
"content_type": "text/plain",
|
||||||
"body": [{"name": "message", "type": "string", "required": True}]
|
"body": [{"name": "message", "type": "string", "required": True}],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,11 +228,11 @@ class TestWebhookServiceUnit:
|
|||||||
|
|
||||||
# Test case 2: text/plain without raw content but required - should fail
|
# Test case 2: text/plain without raw content but required - should fail
|
||||||
webhook_data_no_body = {
|
webhook_data_no_body = {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"headers": {"content-type": "text/plain"},
|
"headers": {"content-type": "text/plain"},
|
||||||
"query_params": {},
|
"query_params": {},
|
||||||
"body": {},
|
"body": {},
|
||||||
"files": {}
|
"files": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
result = WebhookService.validate_webhook_request(webhook_data_no_body, node_config)
|
result = WebhookService.validate_webhook_request(webhook_data_no_body, node_config)
|
||||||
@ -241,11 +241,11 @@ class TestWebhookServiceUnit:
|
|||||||
|
|
||||||
# Test case 3: text/plain with empty raw content but required - should fail
|
# Test case 3: text/plain with empty raw content but required - should fail
|
||||||
webhook_data_empty_body = {
|
webhook_data_empty_body = {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"headers": {"content-type": "text/plain"},
|
"headers": {"content-type": "text/plain"},
|
||||||
"query_params": {},
|
"query_params": {},
|
||||||
"body": {"raw": ""},
|
"body": {"raw": ""},
|
||||||
"files": {}
|
"files": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
result = WebhookService.validate_webhook_request(webhook_data_empty_body, node_config)
|
result = WebhookService.validate_webhook_request(webhook_data_empty_body, node_config)
|
||||||
@ -255,18 +255,18 @@ class TestWebhookServiceUnit:
|
|||||||
def test_validate_webhook_request_text_plain_no_body_params(self):
|
def test_validate_webhook_request_text_plain_no_body_params(self):
|
||||||
"""Test webhook validation for text/plain content type with no body params configured."""
|
"""Test webhook validation for text/plain content type with no body params configured."""
|
||||||
webhook_data = {
|
webhook_data = {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"headers": {"content-type": "text/plain"},
|
"headers": {"content-type": "text/plain"},
|
||||||
"query_params": {},
|
"query_params": {},
|
||||||
"body": {"raw": "Hello World"},
|
"body": {"raw": "Hello World"},
|
||||||
"files": {}
|
"files": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
node_config = {
|
node_config = {
|
||||||
"data": {
|
"data": {
|
||||||
"method": "post",
|
"method": "post",
|
||||||
"content_type": "text/plain",
|
"content_type": "text/plain",
|
||||||
"body": [] # No body params configured
|
"body": [], # No body params configured
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,3 +435,206 @@ class TestWebhookServiceUnit:
|
|||||||
|
|
||||||
# Should skip files without filenames
|
# Should skip files without filenames
|
||||||
assert len(result) == 0
|
assert len(result) == 0
|
||||||
|
|
||||||
|
def test_validate_json_parameter_type_string(self):
|
||||||
|
"""Test JSON parameter type validation for string type."""
|
||||||
|
# Valid string
|
||||||
|
result = WebhookService._validate_json_parameter_type("name", "hello", "string")
|
||||||
|
assert result["valid"] is True
|
||||||
|
|
||||||
|
# Invalid string (number)
|
||||||
|
result = WebhookService._validate_json_parameter_type("name", 123, "string")
|
||||||
|
assert result["valid"] is False
|
||||||
|
assert "must be a string, got int" in result["error"]
|
||||||
|
|
||||||
|
def test_validate_json_parameter_type_number(self):
|
||||||
|
"""Test JSON parameter type validation for number type."""
|
||||||
|
# Valid integer
|
||||||
|
result = WebhookService._validate_json_parameter_type("count", 42, "number")
|
||||||
|
assert result["valid"] is True
|
||||||
|
|
||||||
|
# Valid float
|
||||||
|
result = WebhookService._validate_json_parameter_type("price", 19.99, "number")
|
||||||
|
assert result["valid"] is True
|
||||||
|
|
||||||
|
# Invalid number (string)
|
||||||
|
result = WebhookService._validate_json_parameter_type("count", "42", "number")
|
||||||
|
assert result["valid"] is False
|
||||||
|
assert "must be a number, got str" in result["error"]
|
||||||
|
|
||||||
|
def test_validate_json_parameter_type_bool(self):
|
||||||
|
"""Test JSON parameter type validation for boolean type."""
|
||||||
|
# Valid boolean
|
||||||
|
result = WebhookService._validate_json_parameter_type("enabled", True, "boolean")
|
||||||
|
assert result["valid"] is True
|
||||||
|
|
||||||
|
result = WebhookService._validate_json_parameter_type("enabled", False, "boolean")
|
||||||
|
assert result["valid"] is True
|
||||||
|
|
||||||
|
# Invalid boolean (string)
|
||||||
|
result = WebhookService._validate_json_parameter_type("enabled", "true", "boolean")
|
||||||
|
assert result["valid"] is False
|
||||||
|
assert "must be a boolean, got str" in result["error"]
|
||||||
|
|
||||||
|
def test_validate_json_parameter_type_object(self):
|
||||||
|
"""Test JSON parameter type validation for object type."""
|
||||||
|
# Valid object
|
||||||
|
result = WebhookService._validate_json_parameter_type("user", {"name": "John", "age": 30}, "object")
|
||||||
|
assert result["valid"] is True
|
||||||
|
|
||||||
|
# Invalid object (string)
|
||||||
|
result = WebhookService._validate_json_parameter_type("user", "not_an_object", "object")
|
||||||
|
assert result["valid"] is False
|
||||||
|
assert "must be an object, got str" in result["error"]
|
||||||
|
|
||||||
|
def test_validate_json_parameter_type_array_string(self):
|
||||||
|
"""Test JSON parameter type validation for array[string] type."""
|
||||||
|
# Valid array of strings
|
||||||
|
result = WebhookService._validate_json_parameter_type("tags", ["tag1", "tag2", "tag3"], "array[string]")
|
||||||
|
assert result["valid"] is True
|
||||||
|
|
||||||
|
# Invalid - not an array
|
||||||
|
result = WebhookService._validate_json_parameter_type("tags", "not_an_array", "array[string]")
|
||||||
|
assert result["valid"] is False
|
||||||
|
assert "must be an array, got str" in result["error"]
|
||||||
|
|
||||||
|
# Invalid - array with non-strings
|
||||||
|
result = WebhookService._validate_json_parameter_type("tags", ["tag1", 123, "tag3"], "array[string]")
|
||||||
|
assert result["valid"] is False
|
||||||
|
assert "must be an array of strings" in result["error"]
|
||||||
|
|
||||||
|
def test_validate_json_parameter_type_array_number(self):
|
||||||
|
"""Test JSON parameter type validation for array[number] type."""
|
||||||
|
# Valid array of numbers
|
||||||
|
result = WebhookService._validate_json_parameter_type("scores", [1, 2.5, 3, 4.7], "array[number]")
|
||||||
|
assert result["valid"] is True
|
||||||
|
|
||||||
|
# Invalid - array with non-numbers
|
||||||
|
result = WebhookService._validate_json_parameter_type("scores", [1, "2", 3], "array[number]")
|
||||||
|
assert result["valid"] is False
|
||||||
|
assert "must be an array of numbers" in result["error"]
|
||||||
|
|
||||||
|
def test_validate_json_parameter_type_array_bool(self):
|
||||||
|
"""Test JSON parameter type validation for array[bool] type."""
|
||||||
|
# Valid array of booleans
|
||||||
|
result = WebhookService._validate_json_parameter_type("flags", [True, False, True], "array[boolean]")
|
||||||
|
assert result["valid"] is True
|
||||||
|
|
||||||
|
# Invalid - array with non-booleans
|
||||||
|
result = WebhookService._validate_json_parameter_type("flags", [True, "false", True], "array[boolean]")
|
||||||
|
assert result["valid"] is False
|
||||||
|
assert "must be an array of booleans" in result["error"]
|
||||||
|
|
||||||
|
def test_validate_json_parameter_type_array_object(self):
|
||||||
|
"""Test JSON parameter type validation for array[object] type."""
|
||||||
|
# Valid array of objects
|
||||||
|
result = WebhookService._validate_json_parameter_type(
|
||||||
|
"users", [{"name": "John"}, {"name": "Jane"}], "array[object]"
|
||||||
|
)
|
||||||
|
assert result["valid"] is True
|
||||||
|
|
||||||
|
# Invalid - array with non-objects
|
||||||
|
result = WebhookService._validate_json_parameter_type(
|
||||||
|
"users", [{"name": "John"}, "not_object"], "array[object]"
|
||||||
|
)
|
||||||
|
assert result["valid"] is False
|
||||||
|
assert "must be an array of objects" in result["error"]
|
||||||
|
|
||||||
|
def test_validate_json_parameter_type_unknown_type(self):
|
||||||
|
"""Test JSON parameter type validation for unknown type."""
|
||||||
|
# Unknown type should return valid and log warning
|
||||||
|
result = WebhookService._validate_json_parameter_type("data", "anything", "unknown_type")
|
||||||
|
assert result["valid"] is True
|
||||||
|
|
||||||
|
def test_validate_webhook_request_json_type_validation(self):
|
||||||
|
"""Test webhook validation with JSON parameter type validation."""
|
||||||
|
# Test valid JSON types
|
||||||
|
webhook_data = {
|
||||||
|
"method": "POST",
|
||||||
|
"headers": {"Content-Type": "application/json"},
|
||||||
|
"query_params": {},
|
||||||
|
"body": {
|
||||||
|
"name": "John",
|
||||||
|
"age": 30,
|
||||||
|
"active": True,
|
||||||
|
"profile": {"email": "john@example.com"},
|
||||||
|
"tags": ["developer", "python"],
|
||||||
|
"scores": [85, 92.5, 78],
|
||||||
|
"flags": [True, False],
|
||||||
|
"items": [{"id": 1}, {"id": 2}],
|
||||||
|
},
|
||||||
|
"files": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
node_config = {
|
||||||
|
"data": {
|
||||||
|
"method": "post",
|
||||||
|
"content_type": "application/json",
|
||||||
|
"body": [
|
||||||
|
{"name": "name", "type": "string", "required": True},
|
||||||
|
{"name": "age", "type": "number", "required": True},
|
||||||
|
{"name": "active", "type": "bool", "required": True},
|
||||||
|
{"name": "profile", "type": "object", "required": True},
|
||||||
|
{"name": "tags", "type": "array[string]", "required": True},
|
||||||
|
{"name": "scores", "type": "array[number]", "required": True},
|
||||||
|
{"name": "flags", "type": "array[bool]", "required": True},
|
||||||
|
{"name": "items", "type": "array[object]", "required": True},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = WebhookService.validate_webhook_request(webhook_data, node_config)
|
||||||
|
assert result["valid"] is True
|
||||||
|
|
||||||
|
def test_validate_webhook_request_json_type_validation_invalid(self):
|
||||||
|
"""Test webhook validation with invalid JSON parameter types."""
|
||||||
|
webhook_data = {
|
||||||
|
"method": "POST",
|
||||||
|
"headers": {"Content-Type": "application/json"},
|
||||||
|
"query_params": {},
|
||||||
|
"body": {
|
||||||
|
"name": 123, # Should be string
|
||||||
|
"age": "thirty", # Should be number
|
||||||
|
},
|
||||||
|
"files": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
node_config = {
|
||||||
|
"data": {
|
||||||
|
"method": "post",
|
||||||
|
"content_type": "application/json",
|
||||||
|
"body": [
|
||||||
|
{"name": "name", "type": "string", "required": True},
|
||||||
|
{"name": "age", "type": "number", "required": True},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = WebhookService.validate_webhook_request(webhook_data, node_config)
|
||||||
|
assert result["valid"] is False
|
||||||
|
assert "must be a string, got int" in result["error"]
|
||||||
|
|
||||||
|
def test_validate_webhook_request_non_json_skip_type_validation(self):
|
||||||
|
"""Test that type validation is skipped for non-JSON content types."""
|
||||||
|
webhook_data = {
|
||||||
|
"method": "POST",
|
||||||
|
"headers": {"Content-Type": "application/x-www-form-urlencoded"},
|
||||||
|
"query_params": {},
|
||||||
|
"body": {
|
||||||
|
"name": 123, # Would be invalid for string if this was JSON
|
||||||
|
},
|
||||||
|
"files": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
node_config = {
|
||||||
|
"data": {
|
||||||
|
"method": "post",
|
||||||
|
"content_type": "application/x-www-form-urlencoded",
|
||||||
|
"body": [
|
||||||
|
{"name": "name", "type": "string", "required": True},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = WebhookService.validate_webhook_request(webhook_data, node_config)
|
||||||
|
assert result["valid"] is True # Should pass because type validation is only for JSON
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user