gateway/modules/features/graphicalEditor/nodeRegistry.py
2026-04-11 22:23:41 +02:00

118 lines
4 KiB
Python

# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Node Type Registry for graphicalEditor - static node definitions (ai, email, sharepoint, trigger, flow, data, input).
Nodes are defined first; IO/method actions are used at execution time.
"""
import logging
from typing import Dict, List, Any
from modules.features.graphicalEditor.nodeDefinitions import STATIC_NODE_TYPES
from modules.features.graphicalEditor.portTypes import PORT_TYPE_CATALOG, SYSTEM_VARIABLES
from modules.shared.i18nRegistry import normalizePrimaryLanguageTag
logger = logging.getLogger(__name__)
def getNodeTypes(
services: Any = None,
language: str = "de",
) -> List[Dict[str, Any]]:
"""
Return static node types. No dynamic I/O derivation from methodDiscovery.
services: Optional (kept for API compatibility, not used).
"""
return list(STATIC_NODE_TYPES)
def _pickFromLangMap(d: Any, lang: str) -> Any:
"""Resolve multilingual dict: ``lang`` → ``xx`` → ``de`` → ``en`` → first non-empty value."""
if not isinstance(d, dict) or not d:
return None
for k in (lang, "xx", "de", "en"):
v = d.get(k)
if v is not None and v != "":
return v
for v in d.values():
if v is not None and v != "":
return v
return None
def _localizeNode(node: Dict[str, Any], language: str) -> Dict[str, Any]:
"""Apply language to label/description/parameters. Keep inputPorts/outputPorts."""
lang = normalizePrimaryLanguageTag(language, "en")
out = dict(node)
for key in list(out.keys()):
if key.startswith("_"):
del out[key]
if isinstance(node.get("label"), dict):
out["label"] = _pickFromLangMap(node["label"], lang) or node.get("id", "")
if isinstance(node.get("description"), dict):
out["description"] = _pickFromLangMap(node["description"], lang) or ""
ol = node.get("outputLabels")
if isinstance(ol, dict) and ol:
first = next(iter(ol.values()), None)
if isinstance(first, (list, tuple)):
picked = _pickFromLangMap(ol, lang)
out["outputLabels"] = picked if picked is not None else list(first)
params = []
for p in node.get("parameters", []):
pc = dict(p)
if isinstance(p.get("description"), dict):
pc["description"] = _pickFromLangMap(p["description"], lang) or str(p.get("description", ""))
params.append(pc)
out["parameters"] = params
return out
def getNodeTypesForApi(
services: Any,
language: str = "de",
) -> Dict[str, Any]:
"""
API-ready response: nodeTypes with localized strings, plus categories, portTypeCatalog, systemVariables.
"""
nodes = getNodeTypes(services, language)
localized = [_localizeNode(n, language) for n in nodes]
categories = [
{"id": "trigger", "label": "Trigger"},
{"id": "input", "label": "Eingabe/Mensch"},
{"id": "flow", "label": "Ablauf"},
{"id": "data", "label": "Daten"},
{"id": "ai", "label": "KI"},
{"id": "file", "label": "Datei"},
{"id": "email", "label": "E-Mail"},
{"id": "sharepoint", "label": "SharePoint"},
{"id": "clickup", "label": "ClickUp"},
{"id": "trustee", "label": "Treuhand"},
]
catalogSerialized = {}
for name, schema in PORT_TYPE_CATALOG.items():
catalogSerialized[name] = {
"name": schema.name,
"fields": [f.model_dump() for f in schema.fields],
}
return {
"nodeTypes": localized,
"categories": categories,
"portTypeCatalog": catalogSerialized,
"systemVariables": SYSTEM_VARIABLES,
}
def getNodeTypeToMethodAction() -> Dict[str, tuple]:
"""
Mapping from node type id to (method, action) for execution.
Used by ActionNodeExecutor.
"""
mapping = {}
for node in STATIC_NODE_TYPES:
method = node.get("_method")
action = node.get("_action")
if method and action:
mapping[node["id"]] = (method, action)
return mapping