# Copyright (c) 2025 Patrick Motsch # All rights reserved. """ Automation2 routes - node-types, execute, info. """ import logging from fastapi import APIRouter, Depends, Path, Query, Body from modules.auth import limiter, getRequestContext, RequestContext from modules.features.automation2.mainAutomation2 import getAutomation2Services from modules.features.automation2.nodeRegistry import getNodeTypesForApi from modules.workflows.automation2.executionEngine import executeGraph logger = logging.getLogger(__name__) router = APIRouter( prefix="/api/automation2", tags=["Automation2"], responses={404: {"description": "Not found"}, 403: {"description": "Forbidden"}}, ) def _validateInstanceAccess(instanceId: str, context: RequestContext) -> str: """Validate user has access to the automation2 feature instance. Returns mandateId.""" from fastapi import HTTPException from modules.interfaces.interfaceDbApp import getRootInterface rootInterface = getRootInterface() instance = rootInterface.getFeatureInstance(instanceId) if not instance: raise HTTPException(status_code=404, detail=f"Feature instance {instanceId} not found") featureAccess = rootInterface.getFeatureAccess(str(context.user.id), instanceId) if not featureAccess or not featureAccess.enabled: raise HTTPException(status_code=403, detail="Access denied to this feature instance") return str(instance.mandateId) if instance.mandateId else "" @router.get("/{instanceId}/info") @limiter.limit("60/minute") def get_automation2_info( instanceId: str = Path(..., description="Feature instance ID"), context: RequestContext = Depends(getRequestContext), ) -> dict: """Minimal info endpoint - proves the feature works.""" _validateInstanceAccess(instanceId, context) return { "featureCode": "automation2", "instanceId": instanceId, "status": "ok", "message": "Automation2 feature ready. Build from here.", } @router.get("/{instanceId}/node-types") @limiter.limit("60/minute") def get_node_types( instanceId: str = Path(..., description="Feature instance ID"), language: str = Query("en", description="Localization (en, de, fr)"), context: RequestContext = Depends(getRequestContext), ) -> dict: """Return node types for the flow builder: static + I/O from methodDiscovery.""" mandateId = _validateInstanceAccess(instanceId, context) services = getAutomation2Services( context.user, mandateId=mandateId, featureInstanceId=instanceId, ) return getNodeTypesForApi(services, language=language) @router.post("/{instanceId}/execute") @limiter.limit("30/minute") async def post_execute( instanceId: str = Path(..., description="Feature instance ID"), body: dict = Body(..., description="{ workflowId?, graph: { nodes, connections } }"), context: RequestContext = Depends(getRequestContext), ) -> dict: """Execute automation2 graph. Body: { workflowId?, graph: { nodes, connections } }.""" mandateId = _validateInstanceAccess(instanceId, context) services = getAutomation2Services( context.user, mandateId=mandateId, featureInstanceId=instanceId, ) graph = body.get("graph") or body workflowId = body.get("workflowId") result = await executeGraph( graph=graph, services=services, workflowId=workflowId, instanceId=instanceId, userId=str(context.user.id) if context.user else None, mandateId=mandateId, ) return result