feat(fase4): add JavaScript and HTTP request nodes
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,3 +3,4 @@ from app.nodes.basic import (
|
|||||||
TriggerExecutor, MessageExecutor, ButtonsExecutor,
|
TriggerExecutor, MessageExecutor, ButtonsExecutor,
|
||||||
WaitInputExecutor, SetVariableExecutor, ConditionExecutor
|
WaitInputExecutor, SetVariableExecutor, ConditionExecutor
|
||||||
)
|
)
|
||||||
|
from app.nodes.script import JavaScriptExecutor, HttpRequestExecutor
|
||||||
|
|||||||
100
services/flow-engine/app/nodes/script.py
Normal file
100
services/flow-engine/app/nodes/script.py
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import json
|
||||||
|
from typing import Optional, Any
|
||||||
|
import httpx
|
||||||
|
from app.nodes.base import NodeExecutor
|
||||||
|
from app.context import FlowContext
|
||||||
|
|
||||||
|
|
||||||
|
class JavaScriptExecutor(NodeExecutor):
|
||||||
|
"""Execute Python expressions with restricted globals"""
|
||||||
|
|
||||||
|
ALLOWED_BUILTINS = {
|
||||||
|
'abs': abs, 'all': all, 'any': any, 'bool': bool,
|
||||||
|
'dict': dict, 'float': float, 'int': int, 'len': len,
|
||||||
|
'list': list, 'max': max, 'min': min, 'round': round,
|
||||||
|
'str': str, 'sum': sum, 'sorted': sorted,
|
||||||
|
}
|
||||||
|
|
||||||
|
async def execute(self, config: dict, context: FlowContext, session: Any) -> Optional[str]:
|
||||||
|
code = config.get("code", "")
|
||||||
|
output_variable = config.get("output_variable", "_result")
|
||||||
|
|
||||||
|
if not code:
|
||||||
|
return "default"
|
||||||
|
|
||||||
|
exec_globals = {
|
||||||
|
'__builtins__': self.ALLOWED_BUILTINS,
|
||||||
|
'context': {
|
||||||
|
'contact': context.contact,
|
||||||
|
'conversation': context.conversation,
|
||||||
|
'message': context.message,
|
||||||
|
'variables': context.variables.copy(),
|
||||||
|
},
|
||||||
|
'variables': context.variables.copy(),
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = eval(code, exec_globals, {})
|
||||||
|
if result is not None:
|
||||||
|
context.set(output_variable, result)
|
||||||
|
return "success"
|
||||||
|
except Exception as e:
|
||||||
|
context.set("_script_error", str(e))
|
||||||
|
return "error"
|
||||||
|
|
||||||
|
|
||||||
|
class HttpRequestExecutor(NodeExecutor):
|
||||||
|
"""Make HTTP requests to external APIs"""
|
||||||
|
|
||||||
|
async def execute(self, config: dict, context: FlowContext, session: Any) -> Optional[str]:
|
||||||
|
url = context.interpolate(config.get("url", ""))
|
||||||
|
method = config.get("method", "GET").upper()
|
||||||
|
headers = config.get("headers", {})
|
||||||
|
body = config.get("body")
|
||||||
|
output_variable = config.get("output_variable", "_http_response")
|
||||||
|
timeout = config.get("timeout", 10)
|
||||||
|
|
||||||
|
if not url:
|
||||||
|
return "error"
|
||||||
|
|
||||||
|
headers = {k: context.interpolate(str(v)) for k, v in headers.items()}
|
||||||
|
if body and isinstance(body, str):
|
||||||
|
body = context.interpolate(body)
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
json_body = None
|
||||||
|
if body:
|
||||||
|
try:
|
||||||
|
json_body = json.loads(body) if isinstance(body, str) else body
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
json_body = None
|
||||||
|
|
||||||
|
response = await client.request(
|
||||||
|
method=method,
|
||||||
|
url=url,
|
||||||
|
headers=headers,
|
||||||
|
json=json_body,
|
||||||
|
timeout=timeout
|
||||||
|
)
|
||||||
|
|
||||||
|
response_json = None
|
||||||
|
if response.headers.get("content-type", "").startswith("application/json"):
|
||||||
|
try:
|
||||||
|
response_json = response.json()
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
context.set(output_variable, {
|
||||||
|
"status": response.status_code,
|
||||||
|
"body": response.text,
|
||||||
|
"json": response_json
|
||||||
|
})
|
||||||
|
|
||||||
|
if 200 <= response.status_code < 300:
|
||||||
|
return "success"
|
||||||
|
return "error"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
context.set("_http_error", str(e))
|
||||||
|
return "error"
|
||||||
Reference in New Issue
Block a user