feat(fase4): add validation nodes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude AI
2026-01-29 11:13:36 +00:00
parent 058f837ecb
commit 26e5ce5a31

View File

@@ -0,0 +1,82 @@
import re
from typing import Optional, Any
from datetime import datetime
from app.nodes.base import NodeExecutor
from app.context import FlowContext
class ValidateEmailExecutor(NodeExecutor):
async def execute(self, config: dict, context: FlowContext, session: Any) -> Optional[str]:
variable = config.get("variable", "")
value = context.get(variable) or context.message.get("content", "")
email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if re.match(email_pattern, str(value).strip()):
return "valid"
return "invalid"
class ValidatePhoneExecutor(NodeExecutor):
async def execute(self, config: dict, context: FlowContext, session: Any) -> Optional[str]:
variable = config.get("variable", "")
value = context.get(variable) or context.message.get("content", "")
cleaned = re.sub(r'[\s\-\(\)\+]', '', str(value))
if cleaned.isdigit() and 8 <= len(cleaned) <= 15:
return "valid"
return "invalid"
class ValidateNumberExecutor(NodeExecutor):
async def execute(self, config: dict, context: FlowContext, session: Any) -> Optional[str]:
variable = config.get("variable", "")
value = context.get(variable) or context.message.get("content", "")
min_val = config.get("min")
max_val = config.get("max")
try:
num = float(str(value).strip())
if min_val is not None and num < float(min_val):
return "invalid"
if max_val is not None and num > float(max_val):
return "invalid"
return "valid"
except ValueError:
return "invalid"
class ValidateDateExecutor(NodeExecutor):
async def execute(self, config: dict, context: FlowContext, session: Any) -> Optional[str]:
variable = config.get("variable", "")
value = context.get(variable) or context.message.get("content", "")
date_format = config.get("format", "%Y-%m-%d")
try:
datetime.strptime(str(value).strip(), date_format)
return "valid"
except ValueError:
return "invalid"
class ValidateRegexExecutor(NodeExecutor):
async def execute(self, config: dict, context: FlowContext, session: Any) -> Optional[str]:
variable = config.get("variable", "")
value = context.get(variable) or context.message.get("content", "")
pattern = config.get("pattern", ".*")
try:
if re.match(pattern, str(value).strip()):
return "valid"
return "invalid"
except re.error:
return "invalid"
class ValidateOptionsExecutor(NodeExecutor):
async def execute(self, config: dict, context: FlowContext, session: Any) -> Optional[str]:
variable = config.get("variable", "")
value = context.get(variable) or context.message.get("content", "")
options = config.get("options", [])
case_sensitive = config.get("case_sensitive", False)
value_str = str(value).strip()
if not case_sensitive:
value_str = value_str.lower()
options = [str(o).lower() for o in options]
if value_str in options:
return "valid"
return "invalid"