From 8d28f6d77b3294b387c170626c5f0c3e32e811df Mon Sep 17 00:00:00 2001
From: patrick-motsch
Date: Sun, 8 Feb 2026 14:26:01 +0100
Subject: [PATCH] fiixed feature instance role access
---
.../automation/interfaceFeatureAutomation.py | 19 +-
.../automation/routeFeatureAutomation.py | 28 +-
.../features/chatbot/routeFeatureChatbot.py | 14 +-
.../routeFeatureChatplayground.py | 14 +-
.../neutralization/routeFeatureNeutralizer.py | 16 +-
.../realEstate/routeFeatureRealEstate.py | 58 +--
.../features/trustee/routeFeatureTrustee.py | 232 +++++------
modules/routes/routeAdmin.py | 8 +-
modules/routes/routeAdminAutomationEvents.py | 4 +-
modules/routes/routeAdminFeatures.py | 34 +-
modules/routes/routeAdminRbacExport.py | 4 +-
modules/routes/routeAdminRbacRoles.py | 24 +-
modules/routes/routeAdminRbacRules.py | 34 +-
.../routes/routeAdminUserAccessOverview.py | 6 +-
modules/routes/routeAttributes.py | 4 +-
modules/routes/routeBilling.py | 32 +-
modules/routes/routeDataConnections.py | 14 +-
modules/routes/routeDataFiles.py | 14 +-
modules/routes/routeDataMandates.py | 18 +-
modules/routes/routeDataPrompts.py | 10 +-
modules/routes/routeDataUsers.py | 18 +-
modules/routes/routeDataWorkflows.py | 24 +-
modules/routes/routeGdpr.py | 8 +-
modules/routes/routeInvitations.py | 10 +-
modules/routes/routeMessaging.py | 28 +-
modules/routes/routeNotifications.py | 16 +-
modules/routes/routeSecurityAdmin.py | 20 +-
modules/routes/routeSecurityGoogle.py | 8 +-
modules/routes/routeSecurityLocal.py | 16 +-
modules/routes/routeSecurityMsft.py | 12 +-
modules/routes/routeSystem.py | 2 +-
modules/workflows/automation/mainWorkflow.py | 14 +-
scripts/migrate_async_to_sync.py | 377 ++++++++++++++++++
33 files changed, 764 insertions(+), 376 deletions(-)
create mode 100644 scripts/migrate_async_to_sync.py
diff --git a/modules/features/automation/interfaceFeatureAutomation.py b/modules/features/automation/interfaceFeatureAutomation.py
index f88c3973..770d5eb0 100644
--- a/modules/features/automation/interfaceFeatureAutomation.py
+++ b/modules/features/automation/interfaceFeatureAutomation.py
@@ -88,7 +88,9 @@ class AutomationObjects:
permissions = self.rbac.getUserPermissions(
user=self.currentUser,
context=AccessRuleContext.DATA,
- item=objectKey
+ item=objectKey,
+ mandateId=self.mandateId,
+ featureInstanceId=self.featureInstanceId
)
accessLevel = getattr(permissions, action, AccessLevel.NONE)
@@ -373,6 +375,21 @@ class AutomationObjects:
logger.error(f"Error creating automation definition: {str(e)}")
raise
+ def _saveExecutionLog(self, automationId: str, executionLogs: List[Dict[str, Any]]) -> None:
+ """
+ Save execution logs to an automation definition WITHOUT RBAC check.
+
+ This is a system-level operation: when a user executes an automation,
+ the execution log must be saved regardless of whether the user has
+ 'update' permission on the AutomationDefinition. The user already
+ proved they have execute/read access by loading the automation.
+ """
+ try:
+ self.db.recordModify(AutomationDefinition, automationId, {"executionLogs": executionLogs})
+ logger.debug(f"Saved execution log for automation {automationId}")
+ except Exception as e:
+ logger.warning(f"Could not save execution log for automation {automationId}: {e}")
+
def updateAutomationDefinition(self, automationId: str, automationData: Dict[str, Any]) -> AutomationDefinition:
"""Updates an automation definition, then triggers sync."""
try:
diff --git a/modules/features/automation/routeFeatureAutomation.py b/modules/features/automation/routeFeatureAutomation.py
index d9c0b758..d39a3358 100644
--- a/modules/features/automation/routeFeatureAutomation.py
+++ b/modules/features/automation/routeFeatureAutomation.py
@@ -42,7 +42,7 @@ router = APIRouter(
@router.get("", response_model=PaginatedResponse[AutomationDefinition])
@limiter.limit("30/minute")
-async def get_automations(
+def get_automations(
request: Request,
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
context: RequestContext = Depends(getRequestContext)
@@ -107,7 +107,7 @@ async def get_automations(
@router.post("", response_model=AutomationDefinition)
@limiter.limit("10/minute")
-async def create_automation(
+def create_automation(
request: Request,
automation: AutomationDefinition,
context: RequestContext = Depends(getRequestContext)
@@ -128,7 +128,7 @@ async def create_automation(
)
@router.get("/attributes", response_model=Dict[str, Any])
-async def get_automation_attributes(
+def get_automation_attributes(
request: Request
) -> Dict[str, Any]:
"""Get attribute definitions for AutomationDefinition model"""
@@ -137,7 +137,7 @@ async def get_automation_attributes(
@router.get("/actions")
@limiter.limit("30/minute")
-async def get_available_actions(
+def get_available_actions(
request: Request,
context: RequestContext = Depends(getRequestContext)
) -> JSONResponse:
@@ -230,7 +230,7 @@ async def get_available_actions(
@router.get("/{automationId}", response_model=AutomationDefinition)
@limiter.limit("30/minute")
-async def get_automation(
+def get_automation(
request: Request,
automationId: str = Path(..., description="Automation ID"),
context: RequestContext = Depends(getRequestContext)
@@ -257,7 +257,7 @@ async def get_automation(
@router.put("/{automationId}", response_model=AutomationDefinition)
@limiter.limit("10/minute")
-async def update_automation(
+def update_automation(
request: Request,
automationId: str = Path(..., description="Automation ID"),
automation: AutomationDefinition = Body(...),
@@ -285,7 +285,7 @@ async def update_automation(
@router.patch("/{automationId}/status")
@limiter.limit("30/minute")
-async def update_automation_status(
+def update_automation_status(
request: Request,
automationId: str = Path(..., description="Automation ID"),
active: bool = Body(..., embed=True),
@@ -326,7 +326,7 @@ async def update_automation_status(
@router.delete("/{automationId}")
@limiter.limit("10/minute")
-async def delete_automation(
+def delete_automation(
request: Request,
automationId: str = Path(..., description="Automation ID"),
context: RequestContext = Depends(getRequestContext)
@@ -407,7 +407,7 @@ templateAttributes = getModelAttributeDefinitions(AutomationTemplate)
@templateRouter.get("", response_model=PaginatedResponse[AutomationTemplate])
@limiter.limit("30/minute")
-async def get_db_templates(
+def get_db_templates(
request: Request,
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
context: RequestContext = Depends(getRequestContext)
@@ -470,7 +470,7 @@ async def get_db_templates(
@templateRouter.get("/attributes", response_model=Dict[str, Any])
-async def get_template_attributes(
+def get_template_attributes(
request: Request
) -> Dict[str, Any]:
"""Get attribute definitions for AutomationTemplate model"""
@@ -479,7 +479,7 @@ async def get_template_attributes(
@templateRouter.get("/{templateId}")
@limiter.limit("30/minute")
-async def get_db_template(
+def get_db_template(
request: Request,
templateId: str = Path(..., description="Template ID"),
context: RequestContext = Depends(getRequestContext)
@@ -511,7 +511,7 @@ async def get_db_template(
@templateRouter.post("")
@limiter.limit("10/minute")
-async def create_db_template(
+def create_db_template(
request: Request,
templateData: Dict[str, Any] = Body(...),
context: RequestContext = Depends(getRequestContext)
@@ -542,7 +542,7 @@ async def create_db_template(
@templateRouter.put("/{templateId}")
@limiter.limit("10/minute")
-async def update_db_template(
+def update_db_template(
request: Request,
templateId: str = Path(..., description="Template ID"),
templateData: Dict[str, Any] = Body(...),
@@ -574,7 +574,7 @@ async def update_db_template(
@templateRouter.delete("/{templateId}")
@limiter.limit("10/minute")
-async def delete_db_template(
+def delete_db_template(
request: Request,
templateId: str = Path(..., description="Template ID"),
context: RequestContext = Depends(getRequestContext)
diff --git a/modules/features/chatbot/routeFeatureChatbot.py b/modules/features/chatbot/routeFeatureChatbot.py
index 62dc02f9..d8b4dd70 100644
--- a/modules/features/chatbot/routeFeatureChatbot.py
+++ b/modules/features/chatbot/routeFeatureChatbot.py
@@ -55,7 +55,7 @@ def _getServiceChat(context: RequestContext, instanceId: Optional[str] = None):
)
-async def _validateInstanceAccess(instanceId: str, context: RequestContext) -> str:
+def _validateInstanceAccess(instanceId: str, context: RequestContext) -> str:
"""
Validate that the user has access to the feature instance.
Returns the mandateId for the instance.
@@ -124,7 +124,7 @@ async def stream_chatbot_start(
- Query parameter takes precedence if both are provided
"""
# Validate instance access
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
event_manager = get_event_manager()
@@ -323,7 +323,7 @@ async def stop_chatbot(
) -> ChatWorkflow:
"""Stops a running chatbot workflow."""
# Validate instance access
- await _validateInstanceAccess(instanceId, context)
+ _validateInstanceAccess(instanceId, context)
try:
# Get chatbot interface with instance context
@@ -392,7 +392,7 @@ async def stop_chatbot(
# to prevent "threads" from being matched as a workflowId
@router.get("/{instanceId}/threads")
@limiter.limit("120/minute")
-async def get_chatbot_threads(
+def get_chatbot_threads(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
workflowId: Optional[str] = Query(None, description="Optional workflow ID to get details and chat data for a specific thread"),
@@ -406,7 +406,7 @@ async def get_chatbot_threads(
- If workflowId is not provided: Returns a paginated list of all workflows
"""
# Validate instance access
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
try:
interfaceDbChat = _getServiceChat(context, instanceId)
@@ -523,7 +523,7 @@ async def get_chatbot_threads(
# NOTE: This catch-all route MUST be defined AFTER more specific routes like /threads
@router.delete("/{instanceId}/{workflowId}", response_model=Dict[str, Any])
@limiter.limit("120/minute")
-async def delete_chatbot(
+def delete_chatbot(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
workflowId: str = Path(..., description="ID of the workflow to delete"),
@@ -531,7 +531,7 @@ async def delete_chatbot(
) -> Dict[str, Any]:
"""Deletes a chatbot workflow and its associated data."""
# Validate instance access - if user has access to instance, they can delete their workflows
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
try:
# Get service center
diff --git a/modules/features/chatplayground/routeFeatureChatplayground.py b/modules/features/chatplayground/routeFeatureChatplayground.py
index cedd05d6..ce1611ea 100644
--- a/modules/features/chatplayground/routeFeatureChatplayground.py
+++ b/modules/features/chatplayground/routeFeatureChatplayground.py
@@ -41,7 +41,7 @@ def _getServiceChat(context: RequestContext, featureInstanceId: str = None):
)
-async def _validateInstanceAccess(instanceId: str, context: RequestContext) -> str:
+def _validateInstanceAccess(instanceId: str, context: RequestContext) -> str:
"""
Validate that user has access to the feature instance.
@@ -93,7 +93,7 @@ async def start_workflow(
"""
try:
# Validate access and get mandate ID
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
# Start or continue workflow
workflow = await chatStart(
@@ -129,7 +129,7 @@ async def stop_workflow(
"""Stops a running workflow."""
try:
# Validate access and get mandate ID
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
# Stop workflow (pass featureInstanceId for proper RBAC filtering)
workflow = await chatStop(
@@ -154,7 +154,7 @@ async def stop_workflow(
# Unified Chat Data Endpoint for Polling
@router.get("/{instanceId}/{workflowId}/chatData")
@limiter.limit("120/minute")
-async def get_workflow_chat_data(
+def get_workflow_chat_data(
request: Request,
instanceId: str = Path(..., description="Feature instance ID"),
workflowId: str = Path(..., description="ID of the workflow"),
@@ -167,7 +167,7 @@ async def get_workflow_chat_data(
"""
try:
# Validate access
- await _validateInstanceAccess(instanceId, context)
+ _validateInstanceAccess(instanceId, context)
# Get service with feature instance context
chatInterface = _getServiceChat(context, featureInstanceId=instanceId)
@@ -198,7 +198,7 @@ async def get_workflow_chat_data(
# Get workflows for this instance
@router.get("/{instanceId}/workflows")
@limiter.limit("120/minute")
-async def get_workflows(
+def get_workflows(
request: Request,
instanceId: str = Path(..., description="Feature instance ID"),
page: int = Query(1, ge=1, description="Page number"),
@@ -210,7 +210,7 @@ async def get_workflows(
"""
try:
# Validate access
- await _validateInstanceAccess(instanceId, context)
+ _validateInstanceAccess(instanceId, context)
# Get service with feature instance context
chatInterface = _getServiceChat(context, featureInstanceId=instanceId)
diff --git a/modules/features/neutralization/routeFeatureNeutralizer.py b/modules/features/neutralization/routeFeatureNeutralizer.py
index be262e47..33b9a00d 100644
--- a/modules/features/neutralization/routeFeatureNeutralizer.py
+++ b/modules/features/neutralization/routeFeatureNeutralizer.py
@@ -29,7 +29,7 @@ router = APIRouter(
@router.get("/config", response_model=DataNeutraliserConfig)
@limiter.limit("30/minute")
-async def get_neutralization_config(
+def get_neutralization_config(
request: Request,
context: RequestContext = Depends(getRequestContext)
) -> DataNeutraliserConfig:
@@ -62,7 +62,7 @@ async def get_neutralization_config(
@router.post("/config", response_model=DataNeutraliserConfig)
@limiter.limit("10/minute")
-async def save_neutralization_config(
+def save_neutralization_config(
request: Request,
config_data: Dict[str, Any] = Body(...),
context: RequestContext = Depends(getRequestContext)
@@ -83,7 +83,7 @@ async def save_neutralization_config(
@router.post("/neutralize-text", response_model=Dict[str, Any])
@limiter.limit("20/minute")
-async def neutralize_text(
+def neutralize_text(
request: Request,
text_data: Dict[str, Any] = Body(...),
context: RequestContext = Depends(getRequestContext)
@@ -115,7 +115,7 @@ async def neutralize_text(
@router.post("/resolve-text", response_model=Dict[str, str])
@limiter.limit("20/minute")
-async def resolve_text(
+def resolve_text(
request: Request,
text_data: Dict[str, str] = Body(...),
context: RequestContext = Depends(getRequestContext)
@@ -146,7 +146,7 @@ async def resolve_text(
@router.get("/attributes", response_model=List[DataNeutralizerAttributes])
@limiter.limit("30/minute")
-async def get_neutralization_attributes(
+def get_neutralization_attributes(
request: Request,
fileId: Optional[str] = Query(None, description="Filter by file ID"),
context: RequestContext = Depends(getRequestContext)
@@ -199,7 +199,7 @@ async def process_sharepoint_files(
@router.post("/batch-process", response_model=Dict[str, Any])
@limiter.limit("10/minute")
-async def batch_process_files(
+def batch_process_files(
request: Request,
files_data: List[Dict[str, Any]] = Body(...),
context: RequestContext = Depends(getRequestContext)
@@ -228,7 +228,7 @@ async def batch_process_files(
@router.get("/stats", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def get_neutralization_stats(
+def get_neutralization_stats(
request: Request,
context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
@@ -248,7 +248,7 @@ async def get_neutralization_stats(
@router.delete("/attributes/{fileId}", response_model=Dict[str, str])
@limiter.limit("10/minute")
-async def cleanup_file_attributes(
+def cleanup_file_attributes(
request: Request,
fileId: str = Path(..., description="File ID to cleanup attributes for"),
context: RequestContext = Depends(getRequestContext)
diff --git a/modules/features/realEstate/routeFeatureRealEstate.py b/modules/features/realEstate/routeFeatureRealEstate.py
index 09f28f13..18fecd73 100644
--- a/modules/features/realEstate/routeFeatureRealEstate.py
+++ b/modules/features/realEstate/routeFeatureRealEstate.py
@@ -83,7 +83,7 @@ def _parsePagination(pagination: Optional[str]) -> Optional[PaginationParams]:
return None
-async def _validateInstanceAccess(instanceId: str, context: RequestContext) -> str:
+def _validateInstanceAccess(instanceId: str, context: RequestContext) -> str:
"""
Validate that the user has access to the feature instance.
Returns the mandateId for the instance.
@@ -132,14 +132,14 @@ _REALESTATE_ENTITY_MODELS = {
@router.get("/{instanceId}/attributes/{entityType}", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def get_entity_attributes(
+def get_entity_attributes(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
entityType: str = Path(..., description="Entity type (e.g., Projekt, Parzelle)"),
context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
"""Get attribute definitions for a Real Estate entity. Used by FormGeneratorTable."""
- await _validateInstanceAccess(instanceId, context)
+ _validateInstanceAccess(instanceId, context)
if entityType not in _REALESTATE_ENTITY_MODELS:
raise HTTPException(
status_code=404,
@@ -163,13 +163,13 @@ async def get_entity_attributes(
@router.get("/{instanceId}/projects/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_project_options(
+def get_project_options(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
"""Get project options for select dropdowns. Returns: [{ value, label }]"""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getRealEstateInterface(
context.user, mandateId=mandateId, featureInstanceId=instanceId
)
@@ -179,13 +179,13 @@ async def get_project_options(
@router.get("/{instanceId}/parcels/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_parcel_options(
+def get_parcel_options(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
"""Get parcel options for select dropdowns. Returns: [{ value, label }]"""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getRealEstateInterface(
context.user, mandateId=mandateId, featureInstanceId=instanceId
)
@@ -197,14 +197,14 @@ async def get_parcel_options(
@router.get("/{instanceId}/projects", response_model=PaginatedResponse[Projekt])
@limiter.limit("30/minute")
-async def get_projects(
+def get_projects(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams"),
context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[Projekt]:
"""Get all projects for a feature instance with optional pagination."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getRealEstateInterface(
context.user, mandateId=mandateId, featureInstanceId=instanceId
)
@@ -241,14 +241,14 @@ async def get_projects(
@router.get("/{instanceId}/projects/{projectId}", response_model=Projekt)
@limiter.limit("30/minute")
-async def get_project_by_id(
+def get_project_by_id(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
projectId: str = Path(..., description="Project ID"),
context: RequestContext = Depends(getRequestContext)
) -> Projekt:
"""Get a single project by ID."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getRealEstateInterface(
context.user, mandateId=mandateId, featureInstanceId=instanceId
)
@@ -260,14 +260,14 @@ async def get_project_by_id(
@router.post("/{instanceId}/projects", response_model=Projekt)
@limiter.limit("30/minute")
-async def create_project(
+def create_project(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
data: Dict[str, Any] = Body(...),
context: RequestContext = Depends(getRequestContext)
) -> Projekt:
"""Create a new project."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getRealEstateInterface(
context.user, mandateId=mandateId, featureInstanceId=instanceId
)
@@ -284,7 +284,7 @@ async def create_project(
@router.put("/{instanceId}/projects/{projectId}", response_model=Projekt)
@limiter.limit("30/minute")
-async def update_project(
+def update_project(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
projectId: str = Path(..., description="Project ID"),
@@ -292,7 +292,7 @@ async def update_project(
context: RequestContext = Depends(getRequestContext)
) -> Projekt:
"""Update a project."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getRealEstateInterface(
context.user, mandateId=mandateId, featureInstanceId=instanceId
)
@@ -307,14 +307,14 @@ async def update_project(
@router.delete("/{instanceId}/projects/{projectId}", status_code=status.HTTP_204_NO_CONTENT)
@limiter.limit("30/minute")
-async def delete_project(
+def delete_project(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
projectId: str = Path(..., description="Project ID"),
context: RequestContext = Depends(getRequestContext)
) -> None:
"""Delete a project."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getRealEstateInterface(
context.user, mandateId=mandateId, featureInstanceId=instanceId
)
@@ -329,14 +329,14 @@ async def delete_project(
@router.get("/{instanceId}/parcels", response_model=PaginatedResponse[Parzelle])
@limiter.limit("30/minute")
-async def get_parcels(
+def get_parcels(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams"),
context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[Parzelle]:
"""Get all parcels for a feature instance with optional pagination."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getRealEstateInterface(
context.user, mandateId=mandateId, featureInstanceId=instanceId
)
@@ -373,14 +373,14 @@ async def get_parcels(
@router.get("/{instanceId}/parcels/{parcelId}", response_model=Parzelle)
@limiter.limit("30/minute")
-async def get_parcel_by_id(
+def get_parcel_by_id(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
parcelId: str = Path(..., description="Parcel ID"),
context: RequestContext = Depends(getRequestContext)
) -> Parzelle:
"""Get a single parcel by ID."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getRealEstateInterface(
context.user, mandateId=mandateId, featureInstanceId=instanceId
)
@@ -392,14 +392,14 @@ async def get_parcel_by_id(
@router.post("/{instanceId}/parcels", response_model=Parzelle)
@limiter.limit("30/minute")
-async def create_parcel(
+def create_parcel(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
data: Dict[str, Any] = Body(...),
context: RequestContext = Depends(getRequestContext)
) -> Parzelle:
"""Create a new parcel."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getRealEstateInterface(
context.user, mandateId=mandateId, featureInstanceId=instanceId
)
@@ -416,7 +416,7 @@ async def create_parcel(
@router.put("/{instanceId}/parcels/{parcelId}", response_model=Parzelle)
@limiter.limit("30/minute")
-async def update_parcel(
+def update_parcel(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
parcelId: str = Path(..., description="Parcel ID"),
@@ -424,7 +424,7 @@ async def update_parcel(
context: RequestContext = Depends(getRequestContext)
) -> Parzelle:
"""Update a parcel."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getRealEstateInterface(
context.user, mandateId=mandateId, featureInstanceId=instanceId
)
@@ -439,14 +439,14 @@ async def update_parcel(
@router.delete("/{instanceId}/parcels/{parcelId}", status_code=status.HTTP_204_NO_CONTENT)
@limiter.limit("30/minute")
-async def delete_parcel(
+def delete_parcel(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
parcelId: str = Path(..., description="Parcel ID"),
context: RequestContext = Depends(getRequestContext)
) -> None:
"""Delete a parcel."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getRealEstateInterface(
context.user, mandateId=mandateId, featureInstanceId=instanceId
)
@@ -549,7 +549,7 @@ async def process_command(
@router.get("/tables", response_model=Dict[str, Any])
@limiter.limit("120/minute")
-async def get_available_tables(
+def get_available_tables(
request: Request,
context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
@@ -645,7 +645,7 @@ async def get_available_tables(
@router.get("/table/{table}", response_model=PaginatedResponse[Any])
@limiter.limit("120/minute")
-async def get_table_data(
+def get_table_data(
request: Request,
table: str = Path(..., description="Table name (Projekt, Parzelle, Dokument, Gemeinde, Kanton, Land)"),
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
diff --git a/modules/features/trustee/routeFeatureTrustee.py b/modules/features/trustee/routeFeatureTrustee.py
index 43706a10..408871c5 100644
--- a/modules/features/trustee/routeFeatureTrustee.py
+++ b/modules/features/trustee/routeFeatureTrustee.py
@@ -66,7 +66,7 @@ def _parsePagination(pagination: Optional[str]) -> Optional[PaginationParams]:
return None
-async def _validateInstanceAccess(instanceId: str, context: RequestContext) -> str:
+def _validateInstanceAccess(instanceId: str, context: RequestContext) -> str:
"""
Validate that the user has access to the feature instance.
Returns the mandateId for the instance.
@@ -134,7 +134,7 @@ _TRUSTEE_ENTITY_MODELS = {
@router.get("/{instanceId}/attributes/{entityType}")
@limiter.limit("30/minute")
-async def get_entity_attributes(
+def get_entity_attributes(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
entityType: str = Path(..., description="Entity type (e.g., TrusteeDocument)"),
@@ -145,7 +145,7 @@ async def get_entity_attributes(
Used by FormGeneratorTable for dynamic column generation.
"""
# Validate instance access
- await _validateInstanceAccess(instanceId, context)
+ _validateInstanceAccess(instanceId, context)
# Check if entity type is valid
if entityType not in _TRUSTEE_ENTITY_MODELS:
@@ -182,7 +182,7 @@ async def get_entity_attributes(
@router.get("/mime-types/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_mime_type_options(
+def get_mime_type_options(
request: Request,
context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
@@ -217,13 +217,13 @@ async def get_mime_type_options(
@router.get("/{instanceId}/organisations/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_organisation_options(
+def get_organisation_options(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
"""Get organisation options for select dropdowns. Returns: [{ value, label }]"""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
result = interface.getAllOrganisations(None)
items = result.items if hasattr(result, 'items') else result
@@ -232,13 +232,13 @@ async def get_organisation_options(
@router.get("/{instanceId}/roles/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_role_options(
+def get_role_options(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
"""Get role options for select dropdowns. Returns: [{ value, label }]"""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
result = interface.getAllRoles(None)
items = result.items if hasattr(result, 'items') else result
@@ -247,7 +247,7 @@ async def get_role_options(
@router.get("/{instanceId}/contracts/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_contract_options(
+def get_contract_options(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
organisationId: Optional[str] = Query(None, description="Optional: Filter by organisation ID"),
@@ -261,7 +261,7 @@ async def get_contract_options(
Returns: [{ value, label }]
"""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
if organisationId:
@@ -277,13 +277,13 @@ async def get_contract_options(
@router.get("/{instanceId}/documents/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_document_options(
+def get_document_options(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
"""Get document options for select dropdowns. Returns: [{ id, value, label }]"""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
result = interface.getAllDocuments(None)
items = result.items if hasattr(result, 'items') else result
@@ -293,13 +293,13 @@ async def get_document_options(
@router.get("/{instanceId}/positions/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_position_options(
+def get_position_options(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
"""Get position options for select dropdowns. Returns: [{ id, value, label }]"""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
result = interface.getAllPositions(None)
items = result.items if hasattr(result, 'items') else result
@@ -326,14 +326,14 @@ async def get_position_options(
@router.get("/{instanceId}/organisations", response_model=PaginatedResponse[TrusteeOrganisation])
@limiter.limit("30/minute")
-async def get_organisations(
+def get_organisations(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams"),
context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[TrusteeOrganisation]:
"""Get all organisations for a feature instance with optional pagination."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
paginationParams = _parsePagination(pagination)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
@@ -356,14 +356,14 @@ async def get_organisations(
@router.get("/{instanceId}/organisations/{orgId}", response_model=TrusteeOrganisation)
@limiter.limit("30/minute")
-async def get_organisation(
+def get_organisation(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
orgId: str = Path(..., description="Organisation ID"),
context: RequestContext = Depends(getRequestContext)
) -> TrusteeOrganisation:
"""Get a single organisation by ID."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
org = interface.getOrganisation(orgId)
@@ -374,14 +374,14 @@ async def get_organisation(
@router.post("/{instanceId}/organisations", response_model=TrusteeOrganisation, status_code=201)
@limiter.limit("10/minute")
-async def create_organisation(
+def create_organisation(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
data: TrusteeOrganisation = Body(...),
context: RequestContext = Depends(getRequestContext)
) -> TrusteeOrganisation:
"""Create a new organisation."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
result = interface.createOrganisation(data.model_dump())
@@ -392,7 +392,7 @@ async def create_organisation(
@router.put("/{instanceId}/organisations/{orgId}", response_model=TrusteeOrganisation)
@limiter.limit("10/minute")
-async def update_organisation(
+def update_organisation(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
orgId: str = Path(..., description="Organisation ID"),
@@ -400,7 +400,7 @@ async def update_organisation(
context: RequestContext = Depends(getRequestContext)
) -> TrusteeOrganisation:
"""Update an organisation."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
existing = interface.getOrganisation(orgId)
@@ -415,14 +415,14 @@ async def update_organisation(
@router.delete("/{instanceId}/organisations/{orgId}")
@limiter.limit("10/minute")
-async def delete_organisation(
+def delete_organisation(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
orgId: str = Path(..., description="Organisation ID"),
context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
"""Delete an organisation."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
existing = interface.getOrganisation(orgId)
@@ -439,14 +439,14 @@ async def delete_organisation(
@router.get("/{instanceId}/roles", response_model=PaginatedResponse[TrusteeRole])
@limiter.limit("30/minute")
-async def get_roles(
+def get_roles(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
pagination: Optional[str] = Query(None),
context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[TrusteeRole]:
"""Get all roles with optional pagination."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
paginationParams = _parsePagination(pagination)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
@@ -469,14 +469,14 @@ async def get_roles(
@router.get("/{instanceId}/roles/{roleId}", response_model=TrusteeRole)
@limiter.limit("30/minute")
-async def get_role(
+def get_role(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
roleId: str = Path(..., description="Role ID"),
context: RequestContext = Depends(getRequestContext)
) -> TrusteeRole:
"""Get a single role by ID."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
role = interface.getRole(roleId)
@@ -487,14 +487,14 @@ async def get_role(
@router.post("/{instanceId}/roles", response_model=TrusteeRole, status_code=201)
@limiter.limit("10/minute")
-async def create_role(
+def create_role(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
data: TrusteeRole = Body(...),
context: RequestContext = Depends(getRequestContext)
) -> TrusteeRole:
"""Create a new role (sysadmin only)."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
result = interface.createRole(data.model_dump())
@@ -505,7 +505,7 @@ async def create_role(
@router.put("/{instanceId}/roles/{roleId}", response_model=TrusteeRole)
@limiter.limit("10/minute")
-async def update_role(
+def update_role(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
roleId: str = Path(...),
@@ -513,7 +513,7 @@ async def update_role(
context: RequestContext = Depends(getRequestContext)
) -> TrusteeRole:
"""Update a role (sysadmin only)."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
existing = interface.getRole(roleId)
@@ -528,14 +528,14 @@ async def update_role(
@router.delete("/{instanceId}/roles/{roleId}")
@limiter.limit("10/minute")
-async def delete_role(
+def delete_role(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
roleId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
"""Delete a role (sysadmin only, fails if in use)."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
existing = interface.getRole(roleId)
@@ -552,14 +552,14 @@ async def delete_role(
@router.get("/{instanceId}/access", response_model=PaginatedResponse[TrusteeAccess])
@limiter.limit("30/minute")
-async def get_all_access(
+def get_all_access(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
pagination: Optional[str] = Query(None),
context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[TrusteeAccess]:
"""Get all access records with optional pagination."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
paginationParams = _parsePagination(pagination)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
@@ -582,14 +582,14 @@ async def get_all_access(
@router.get("/{instanceId}/access/{accessId}", response_model=TrusteeAccess)
@limiter.limit("30/minute")
-async def get_access(
+def get_access(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
accessId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> TrusteeAccess:
"""Get a single access record by ID."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
access = interface.getAccess(accessId)
@@ -600,14 +600,14 @@ async def get_access(
@router.get("/{instanceId}/access/organisation/{orgId}", response_model=List[TrusteeAccess])
@limiter.limit("30/minute")
-async def get_access_by_organisation(
+def get_access_by_organisation(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
orgId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> List[TrusteeAccess]:
"""Get all access records for an organisation."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
return interface.getAccessByOrganisation(orgId)
@@ -615,14 +615,14 @@ async def get_access_by_organisation(
@router.get("/{instanceId}/access/user/{userId}", response_model=List[TrusteeAccess])
@limiter.limit("30/minute")
-async def get_access_by_user(
+def get_access_by_user(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
userId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> List[TrusteeAccess]:
"""Get all access records for a user."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
return interface.getAccessByUser(userId)
@@ -630,14 +630,14 @@ async def get_access_by_user(
@router.post("/{instanceId}/access", response_model=TrusteeAccess, status_code=201)
@limiter.limit("10/minute")
-async def create_access(
+def create_access(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
data: TrusteeAccess = Body(...),
context: RequestContext = Depends(getRequestContext)
) -> TrusteeAccess:
"""Create a new access record."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
result = interface.createAccess(data.model_dump())
@@ -648,7 +648,7 @@ async def create_access(
@router.put("/{instanceId}/access/{accessId}", response_model=TrusteeAccess)
@limiter.limit("10/minute")
-async def update_access(
+def update_access(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
accessId: str = Path(...),
@@ -656,7 +656,7 @@ async def update_access(
context: RequestContext = Depends(getRequestContext)
) -> TrusteeAccess:
"""Update an access record."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
existing = interface.getAccess(accessId)
@@ -671,14 +671,14 @@ async def update_access(
@router.delete("/{instanceId}/access/{accessId}")
@limiter.limit("10/minute")
-async def delete_access(
+def delete_access(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
accessId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
"""Delete an access record."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
existing = interface.getAccess(accessId)
@@ -695,14 +695,14 @@ async def delete_access(
@router.get("/{instanceId}/contracts", response_model=PaginatedResponse[TrusteeContract])
@limiter.limit("30/minute")
-async def get_contracts(
+def get_contracts(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
pagination: Optional[str] = Query(None),
context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[TrusteeContract]:
"""Get all contracts with optional pagination."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
paginationParams = _parsePagination(pagination)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
@@ -725,14 +725,14 @@ async def get_contracts(
@router.get("/{instanceId}/contracts/{contractId}", response_model=TrusteeContract)
@limiter.limit("30/minute")
-async def get_contract(
+def get_contract(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
contractId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> TrusteeContract:
"""Get a single contract by ID."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
contract = interface.getContract(contractId)
@@ -743,14 +743,14 @@ async def get_contract(
@router.get("/{instanceId}/contracts/organisation/{orgId}", response_model=List[TrusteeContract])
@limiter.limit("30/minute")
-async def get_contracts_by_organisation(
+def get_contracts_by_organisation(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
orgId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> List[TrusteeContract]:
"""Get all contracts for an organisation."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
return interface.getContractsByOrganisation(orgId)
@@ -758,14 +758,14 @@ async def get_contracts_by_organisation(
@router.post("/{instanceId}/contracts", response_model=TrusteeContract, status_code=201)
@limiter.limit("10/minute")
-async def create_contract(
+def create_contract(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
data: TrusteeContract = Body(...),
context: RequestContext = Depends(getRequestContext)
) -> TrusteeContract:
"""Create a new contract."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
result = interface.createContract(data.model_dump())
@@ -776,7 +776,7 @@ async def create_contract(
@router.put("/{instanceId}/contracts/{contractId}", response_model=TrusteeContract)
@limiter.limit("10/minute")
-async def update_contract(
+def update_contract(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
contractId: str = Path(...),
@@ -784,7 +784,7 @@ async def update_contract(
context: RequestContext = Depends(getRequestContext)
) -> TrusteeContract:
"""Update a contract (organisationId is immutable)."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
existing = interface.getContract(contractId)
@@ -799,14 +799,14 @@ async def update_contract(
@router.delete("/{instanceId}/contracts/{contractId}")
@limiter.limit("10/minute")
-async def delete_contract(
+def delete_contract(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
contractId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
"""Delete a contract."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
existing = interface.getContract(contractId)
@@ -823,14 +823,14 @@ async def delete_contract(
@router.get("/{instanceId}/documents", response_model=PaginatedResponse[TrusteeDocument])
@limiter.limit("30/minute")
-async def get_documents(
+def get_documents(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
pagination: Optional[str] = Query(None),
context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[TrusteeDocument]:
"""Get all documents (metadata only) with optional pagination."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
paginationParams = _parsePagination(pagination)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
@@ -853,14 +853,14 @@ async def get_documents(
@router.get("/{instanceId}/documents/{documentId}", response_model=TrusteeDocument)
@limiter.limit("30/minute")
-async def get_document(
+def get_document(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
documentId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> TrusteeDocument:
"""Get document metadata by ID."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
doc = interface.getDocument(documentId)
@@ -871,14 +871,14 @@ async def get_document(
@router.get("/{instanceId}/documents/{documentId}/data")
@limiter.limit("10/minute")
-async def get_document_data(
+def get_document_data(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
documentId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
):
"""Download document binary data."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
doc = interface.getDocument(documentId)
@@ -898,14 +898,14 @@ async def get_document_data(
@router.get("/{instanceId}/documents/contract/{contractId}", response_model=List[TrusteeDocument])
@limiter.limit("30/minute")
-async def get_documents_by_contract(
+def get_documents_by_contract(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
contractId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> List[TrusteeDocument]:
"""Get all documents for a contract."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
return interface.getDocumentsByContract(contractId)
@@ -919,7 +919,7 @@ async def create_document(
context: RequestContext = Depends(getRequestContext)
) -> TrusteeDocument:
"""Create a new document. Accepts JSON body with optional base64-encoded documentData."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
# Parse JSON body
body = await request.json()
@@ -959,7 +959,7 @@ async def upload_document(
context: RequestContext = Depends(getRequestContext)
) -> TrusteeDocument:
"""Upload a document with multipart/form-data."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
# Read file content
fileContent = await file.read()
@@ -980,7 +980,7 @@ async def upload_document(
@router.put("/{instanceId}/documents/{documentId}", response_model=TrusteeDocument)
@limiter.limit("10/minute")
-async def update_document(
+def update_document(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
documentId: str = Path(...),
@@ -988,7 +988,7 @@ async def update_document(
context: RequestContext = Depends(getRequestContext)
) -> TrusteeDocument:
"""Update document metadata."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
existing = interface.getDocument(documentId)
@@ -1003,14 +1003,14 @@ async def update_document(
@router.delete("/{instanceId}/documents/{documentId}")
@limiter.limit("10/minute")
-async def delete_document(
+def delete_document(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
documentId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
"""Delete a document."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
existing = interface.getDocument(documentId)
@@ -1027,14 +1027,14 @@ async def delete_document(
@router.get("/{instanceId}/positions", response_model=PaginatedResponse[TrusteePosition])
@limiter.limit("30/minute")
-async def get_positions(
+def get_positions(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
pagination: Optional[str] = Query(None),
context: RequestContext = Depends(getRequestContext)
) -> PaginatedResponse[TrusteePosition]:
"""Get all positions with optional pagination."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
paginationParams = _parsePagination(pagination)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
@@ -1057,14 +1057,14 @@ async def get_positions(
@router.get("/{instanceId}/positions/{positionId}", response_model=TrusteePosition)
@limiter.limit("30/minute")
-async def get_position(
+def get_position(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
positionId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> TrusteePosition:
"""Get a single position by ID."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
position = interface.getPosition(positionId)
@@ -1075,14 +1075,14 @@ async def get_position(
@router.get("/{instanceId}/positions/contract/{contractId}", response_model=List[TrusteePosition])
@limiter.limit("30/minute")
-async def get_positions_by_contract(
+def get_positions_by_contract(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
contractId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> List[TrusteePosition]:
"""Get all positions for a contract."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
return interface.getPositionsByContract(contractId)
@@ -1090,14 +1090,14 @@ async def get_positions_by_contract(
@router.get("/{instanceId}/positions/organisation/{orgId}", response_model=List[TrusteePosition])
@limiter.limit("30/minute")
-async def get_positions_by_organisation(
+def get_positions_by_organisation(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
orgId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> List[TrusteePosition]:
"""Get all positions for an organisation."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
return interface.getPositionsByOrganisation(orgId)
@@ -1105,14 +1105,14 @@ async def get_positions_by_organisation(
@router.post("/{instanceId}/positions", response_model=TrusteePosition, status_code=201)
@limiter.limit("10/minute")
-async def create_position(
+def create_position(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
data: TrusteePosition = Body(...),
context: RequestContext = Depends(getRequestContext)
) -> TrusteePosition:
"""Create a new position."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
result = interface.createPosition(data.model_dump())
@@ -1123,7 +1123,7 @@ async def create_position(
@router.put("/{instanceId}/positions/{positionId}", response_model=TrusteePosition)
@limiter.limit("10/minute")
-async def update_position(
+def update_position(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
positionId: str = Path(...),
@@ -1131,7 +1131,7 @@ async def update_position(
context: RequestContext = Depends(getRequestContext)
) -> TrusteePosition:
"""Update a position."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
existing = interface.getPosition(positionId)
@@ -1146,14 +1146,14 @@ async def update_position(
@router.delete("/{instanceId}/positions/{positionId}")
@limiter.limit("10/minute")
-async def delete_position(
+def delete_position(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
positionId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
"""Delete a position."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
existing = interface.getPosition(positionId)
@@ -1170,7 +1170,7 @@ async def delete_position(
@router.get("/{instanceId}/position-documents")
@limiter.limit("30/minute")
-async def get_position_documents(
+def get_position_documents(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
pagination: Optional[str] = Query(None),
@@ -1180,7 +1180,7 @@ async def get_position_documents(
Each item includes _permissions: { canUpdate, canDelete } for row-level permission UI.
"""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
paginationParams = _parsePagination(pagination)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
@@ -1203,14 +1203,14 @@ async def get_position_documents(
@router.get("/{instanceId}/position-documents/{linkId}", response_model=TrusteePositionDocument)
@limiter.limit("30/minute")
-async def get_position_document(
+def get_position_document(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
linkId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> TrusteePositionDocument:
"""Get a single position-document link by ID."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
link = interface.getPositionDocument(linkId)
@@ -1221,14 +1221,14 @@ async def get_position_document(
@router.get("/{instanceId}/position-documents/position/{positionId}", response_model=List[TrusteePositionDocument])
@limiter.limit("30/minute")
-async def get_documents_for_position(
+def get_documents_for_position(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
positionId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> List[TrusteePositionDocument]:
"""Get all document links for a position."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
return interface.getDocumentsForPosition(positionId)
@@ -1236,14 +1236,14 @@ async def get_documents_for_position(
@router.get("/{instanceId}/position-documents/document/{documentId}", response_model=List[TrusteePositionDocument])
@limiter.limit("30/minute")
-async def get_positions_for_document(
+def get_positions_for_document(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
documentId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> List[TrusteePositionDocument]:
"""Get all position links for a document."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
return interface.getPositionsForDocument(documentId)
@@ -1251,14 +1251,14 @@ async def get_positions_for_document(
@router.post("/{instanceId}/position-documents", response_model=TrusteePositionDocument, status_code=201)
@limiter.limit("10/minute")
-async def create_position_document(
+def create_position_document(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
data: TrusteePositionDocument = Body(...),
context: RequestContext = Depends(getRequestContext)
) -> TrusteePositionDocument:
"""Create a new position-document link."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
result = interface.createPositionDocument(data.model_dump())
@@ -1269,7 +1269,7 @@ async def create_position_document(
@router.put("/{instanceId}/position-documents/{linkId}", response_model=TrusteePositionDocument)
@limiter.limit("10/minute")
-async def update_position_document(
+def update_position_document(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
linkId: str = Path(...),
@@ -1277,7 +1277,7 @@ async def update_position_document(
context: RequestContext = Depends(getRequestContext)
) -> TrusteePositionDocument:
"""Update a position-document link."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
result = interface.updatePositionDocument(linkId, data.model_dump(exclude_unset=True))
@@ -1288,14 +1288,14 @@ async def update_position_document(
@router.delete("/{instanceId}/position-documents/{linkId}")
@limiter.limit("10/minute")
-async def delete_position_document(
+def delete_position_document(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
linkId: str = Path(...),
context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
"""Delete a position-document link."""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
existing = interface.getPositionDocument(linkId)
@@ -1314,14 +1314,14 @@ async def delete_position_document(
from modules.datamodels.datamodelRbac import Role, AccessRule, AccessRuleContext
-async def _validateInstanceAdmin(instanceId: str, context: RequestContext) -> str:
+def _validateInstanceAdmin(instanceId: str, context: RequestContext) -> str:
"""
Validate that the user has admin access to the feature instance.
Returns the mandateId if authorized.
This checks for the RESOURCE permission 'instance-roles.manage'.
"""
- mandateId = await _validateInstanceAccess(instanceId, context)
+ mandateId = _validateInstanceAccess(instanceId, context)
# SysAdmin always has access
if context.user.isSysAdmin:
@@ -1350,7 +1350,7 @@ async def _validateInstanceAdmin(instanceId: str, context: RequestContext) -> st
@router.get("/{instanceId}/instance-roles", response_model=PaginatedResponse)
@limiter.limit("30/minute")
-async def get_instance_roles(
+def get_instance_roles(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
context: RequestContext = Depends(getRequestContext)
@@ -1359,7 +1359,7 @@ async def get_instance_roles(
Get all roles for this feature instance.
Requires feature admin permission.
"""
- mandateId = await _validateInstanceAdmin(instanceId, context)
+ mandateId = _validateInstanceAdmin(instanceId, context)
rootInterface = getRootInterface()
@@ -1374,14 +1374,14 @@ async def get_instance_roles(
@router.get("/{instanceId}/instance-roles/{roleId}", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def get_instance_role(
+def get_instance_role(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
roleId: str = Path(..., description="Role ID"),
context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]:
"""Get a specific instance role."""
- mandateId = await _validateInstanceAdmin(instanceId, context)
+ mandateId = _validateInstanceAdmin(instanceId, context)
rootInterface = getRootInterface()
role = rootInterface.getRole(roleId)
@@ -1398,7 +1398,7 @@ async def get_instance_role(
@router.get("/{instanceId}/instance-roles/{roleId}/rules", response_model=PaginatedResponse)
@limiter.limit("30/minute")
-async def get_instance_role_rules(
+def get_instance_role_rules(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
roleId: str = Path(..., description="Role ID"),
@@ -1408,7 +1408,7 @@ async def get_instance_role_rules(
Get all AccessRules for a specific instance role.
Requires feature admin permission.
"""
- mandateId = await _validateInstanceAdmin(instanceId, context)
+ mandateId = _validateInstanceAdmin(instanceId, context)
rootInterface = getRootInterface()
@@ -1428,7 +1428,7 @@ async def get_instance_role_rules(
@router.post("/{instanceId}/instance-roles/{roleId}/rules", response_model=Dict[str, Any], status_code=201)
@limiter.limit("10/minute")
-async def create_instance_role_rule(
+def create_instance_role_rule(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
roleId: str = Path(..., description="Role ID"),
@@ -1439,7 +1439,7 @@ async def create_instance_role_rule(
Create a new AccessRule for an instance role.
Requires feature admin permission.
"""
- mandateId = await _validateInstanceAdmin(instanceId, context)
+ mandateId = _validateInstanceAdmin(instanceId, context)
rootInterface = getRootInterface()
@@ -1477,7 +1477,7 @@ async def create_instance_role_rule(
@router.put("/{instanceId}/instance-roles/{roleId}/rules/{ruleId}", response_model=Dict[str, Any])
@limiter.limit("10/minute")
-async def update_instance_role_rule(
+def update_instance_role_rule(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
roleId: str = Path(..., description="Role ID"),
@@ -1490,7 +1490,7 @@ async def update_instance_role_rule(
Only view, read, create, update, delete can be changed.
Requires feature admin permission.
"""
- mandateId = await _validateInstanceAdmin(instanceId, context)
+ mandateId = _validateInstanceAdmin(instanceId, context)
rootInterface = getRootInterface()
@@ -1530,7 +1530,7 @@ async def update_instance_role_rule(
@router.delete("/{instanceId}/instance-roles/{roleId}/rules/{ruleId}")
@limiter.limit("10/minute")
-async def delete_instance_role_rule(
+def delete_instance_role_rule(
request: Request,
instanceId: str = Path(..., description="Feature Instance ID"),
roleId: str = Path(..., description="Role ID"),
@@ -1541,7 +1541,7 @@ async def delete_instance_role_rule(
Delete an AccessRule for an instance role.
Requires feature admin permission.
"""
- mandateId = await _validateInstanceAdmin(instanceId, context)
+ mandateId = _validateInstanceAdmin(instanceId, context)
rootInterface = getRootInterface()
diff --git a/modules/routes/routeAdmin.py b/modules/routes/routeAdmin.py
index 878dbd66..ed5bf42c 100644
--- a/modules/routes/routeAdmin.py
+++ b/modules/routes/routeAdmin.py
@@ -33,7 +33,7 @@ router.mount(
@router.get("/")
@limiter.limit("30/minute")
-async def root(request: Request) -> Dict[str, str]:
+def root(request: Request) -> Dict[str, str]:
"""API status endpoint"""
# Validate required configuration values
allowedOrigins = APP_CONFIG.get("APP_ALLOWED_ORIGINS")
@@ -51,7 +51,7 @@ async def root(request: Request) -> Dict[str, str]:
@router.get("/api/environment")
@limiter.limit("30/minute")
-async def get_environment(
+def get_environment(
request: Request, currentUser: Dict[str, Any] = Depends(getCurrentUser)
) -> Dict[str, str]:
"""Get environment configuration for frontend"""
@@ -82,13 +82,13 @@ async def get_environment(
@router.options("/{fullPath:path}")
@limiter.limit("60/minute")
-async def options_route(request: Request, fullPath: str) -> Response:
+def options_route(request: Request, fullPath: str) -> Response:
return Response(status_code=200)
@router.get("/favicon.ico")
@limiter.limit("30/minute")
-async def favicon(request: Request) -> FileResponse:
+def favicon(request: Request) -> FileResponse:
favicon_path = staticFolder / "favicon.ico"
if not favicon_path.exists():
raise HTTPException(status_code=404, detail="Favicon not found")
diff --git a/modules/routes/routeAdminAutomationEvents.py b/modules/routes/routeAdminAutomationEvents.py
index e8bb9291..7765d621 100644
--- a/modules/routes/routeAdminAutomationEvents.py
+++ b/modules/routes/routeAdminAutomationEvents.py
@@ -33,7 +33,7 @@ router = APIRouter(
@router.get("")
@limiter.limit("30/minute")
-async def get_all_automation_events(
+def get_all_automation_events(
request: Request,
currentUser: User = Depends(requireSysAdmin)
) -> List[Dict[str, Any]]:
@@ -107,7 +107,7 @@ async def sync_all_automation_events(
@router.post("/{eventId}/remove")
@limiter.limit("10/minute")
-async def remove_event(
+def remove_event(
request: Request,
eventId: str = Path(..., description="Event ID to remove"),
currentUser: User = Depends(requireSysAdmin)
diff --git a/modules/routes/routeAdminFeatures.py b/modules/routes/routeAdminFeatures.py
index 87582b9e..3adc8025 100644
--- a/modules/routes/routeAdminFeatures.py
+++ b/modules/routes/routeAdminFeatures.py
@@ -67,7 +67,7 @@ class SyncRolesResult(BaseModel):
@router.get("/", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def list_features(
+def list_features(
request: Request,
context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
@@ -105,7 +105,7 @@ class FeaturesMyResponse(BaseModel):
@router.get("/my", response_model=FeaturesMyResponse)
@limiter.limit("60/minute")
-async def get_my_feature_instances(
+def get_my_feature_instances(
request: Request,
context: RequestContext = Depends(getRequestContext)
) -> FeaturesMyResponse:
@@ -332,7 +332,7 @@ def _mergeAccessLevel(current: str, new: str) -> str:
@router.post("/", response_model=Dict[str, Any])
@limiter.limit("10/minute")
-async def create_feature(
+def create_feature(
request: Request,
code: str = Query(..., description="Unique feature code"),
label: Dict[str, str] = None,
@@ -387,7 +387,7 @@ async def create_feature(
@router.get("/instances", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def list_feature_instances(
+def list_feature_instances(
request: Request,
featureCode: Optional[str] = Query(None, description="Filter by feature code"),
context: RequestContext = Depends(getRequestContext)
@@ -429,7 +429,7 @@ async def list_feature_instances(
@router.get("/instances/{instanceId}", response_model=Dict[str, Any])
@limiter.limit("60/minute")
-async def get_feature_instance(
+def get_feature_instance(
request: Request,
instanceId: str,
context: RequestContext = Depends(getRequestContext)
@@ -473,7 +473,7 @@ async def get_feature_instance(
@router.post("/instances", response_model=Dict[str, Any])
@limiter.limit("10/minute")
-async def create_feature_instance(
+def create_feature_instance(
request: Request,
data: FeatureInstanceCreate,
context: RequestContext = Depends(getRequestContext)
@@ -540,7 +540,7 @@ async def create_feature_instance(
@router.delete("/instances/{instanceId}", response_model=Dict[str, str])
@limiter.limit("10/minute")
-async def delete_feature_instance(
+def delete_feature_instance(
request: Request,
instanceId: str,
context: RequestContext = Depends(getRequestContext)
@@ -605,7 +605,7 @@ class FeatureInstanceUpdate(BaseModel):
@router.put("/instances/{instanceId}", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def updateFeatureInstance(
+def updateFeatureInstance(
request: Request,
instanceId: str,
data: FeatureInstanceUpdate,
@@ -682,7 +682,7 @@ async def updateFeatureInstance(
@router.post("/instances/{instanceId}/sync-roles", response_model=SyncRolesResult)
@limiter.limit("10/minute")
-async def sync_instance_roles(
+def sync_instance_roles(
request: Request,
instanceId: str,
addOnly: bool = Query(True, description="Only add missing roles, don't remove extras"),
@@ -749,7 +749,7 @@ async def sync_instance_roles(
@router.get("/templates/roles", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def list_template_roles(
+def list_template_roles(
request: Request,
featureCode: Optional[str] = Query(None, description="Filter by feature code"),
sysAdmin: User = Depends(requireSysAdmin)
@@ -779,7 +779,7 @@ async def list_template_roles(
@router.post("/templates/roles", response_model=Dict[str, Any])
@limiter.limit("10/minute")
-async def create_template_role(
+def create_template_role(
request: Request,
roleLabel: str = Query(..., description="Role label (e.g., 'admin', 'viewer')"),
featureCode: str = Query(..., description="Feature code this role belongs to"),
@@ -864,7 +864,7 @@ class FeatureInstanceUserUpdate(BaseModel):
@router.get("/instances/{instanceId}/users", response_model=List[FeatureInstanceUserResponse])
@limiter.limit("60/minute")
-async def list_feature_instance_users(
+def list_feature_instance_users(
request: Request,
instanceId: str,
context: RequestContext = Depends(getRequestContext)
@@ -942,7 +942,7 @@ async def list_feature_instance_users(
@router.post("/instances/{instanceId}/users", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def add_user_to_feature_instance(
+def add_user_to_feature_instance(
request: Request,
instanceId: str,
data: FeatureInstanceUserCreate,
@@ -1043,7 +1043,7 @@ async def add_user_to_feature_instance(
@router.delete("/instances/{instanceId}/users/{userId}", response_model=Dict[str, str])
@limiter.limit("30/minute")
-async def remove_user_from_feature_instance(
+def remove_user_from_feature_instance(
request: Request,
instanceId: str,
userId: str,
@@ -1121,7 +1121,7 @@ async def remove_user_from_feature_instance(
@router.put("/instances/{instanceId}/users/{userId}/roles", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def update_feature_instance_user_roles(
+def update_feature_instance_user_roles(
request: Request,
instanceId: str,
userId: str,
@@ -1216,7 +1216,7 @@ async def update_feature_instance_user_roles(
@router.get("/instances/{instanceId}/available-roles", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_feature_instance_available_roles(
+def get_feature_instance_available_roles(
request: Request,
instanceId: str,
context: RequestContext = Depends(getRequestContext)
@@ -1280,7 +1280,7 @@ async def get_feature_instance_available_roles(
@router.get("/{featureCode}", response_model=Dict[str, Any])
@limiter.limit("60/minute")
-async def get_feature(
+def get_feature(
request: Request,
featureCode: str,
context: RequestContext = Depends(getRequestContext)
diff --git a/modules/routes/routeAdminRbacExport.py b/modules/routes/routeAdminRbacExport.py
index 28caf8c8..d22a6ba7 100644
--- a/modules/routes/routeAdminRbacExport.py
+++ b/modules/routes/routeAdminRbacExport.py
@@ -72,7 +72,7 @@ class RbacImportResult(BaseModel):
@router.get("/export/global", response_model=RbacExportData)
@limiter.limit("10/minute")
-async def export_global_rbac(
+def export_global_rbac(
request: Request,
sysAdmin: User = Depends(requireSysAdmin)
) -> RbacExportData:
@@ -281,7 +281,7 @@ async def import_global_rbac(
@router.get("/export/mandate", response_model=RbacExportData)
@limiter.limit("10/minute")
-async def export_mandate_rbac(
+def export_mandate_rbac(
request: Request,
includeFeatureInstances: bool = True,
context: RequestContext = Depends(getRequestContext)
diff --git a/modules/routes/routeAdminRbacRoles.py b/modules/routes/routeAdminRbacRoles.py
index 75e00cd5..97830991 100644
--- a/modules/routes/routeAdminRbacRoles.py
+++ b/modules/routes/routeAdminRbacRoles.py
@@ -68,7 +68,7 @@ router = APIRouter(
@router.get("/", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def list_roles(
+def list_roles(
request: Request,
currentUser: User = Depends(requireSysAdmin)
) -> List[Dict[str, Any]]:
@@ -113,7 +113,7 @@ async def list_roles(
@router.get("/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_role_options(
+def get_role_options(
request: Request,
currentUser: User = Depends(requireSysAdmin)
) -> List[Dict[str, Any]]:
@@ -154,7 +154,7 @@ async def get_role_options(
@router.post("/", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def create_role(
+def create_role(
request: Request,
role: Role = Body(...),
currentUser: User = Depends(requireSysAdmin)
@@ -198,7 +198,7 @@ async def create_role(
@router.get("/{roleId}", response_model=Dict[str, Any])
@limiter.limit("60/minute")
-async def get_role(
+def get_role(
request: Request,
roleId: str = Path(..., description="Role ID"),
currentUser: User = Depends(requireSysAdmin)
@@ -242,7 +242,7 @@ async def get_role(
@router.put("/{roleId}", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def update_role(
+def update_role(
request: Request,
roleId: str = Path(..., description="Role ID"),
role: Role = Body(...),
@@ -290,7 +290,7 @@ async def update_role(
@router.delete("/{roleId}", response_model=Dict[str, str])
@limiter.limit("30/minute")
-async def delete_role(
+def delete_role(
request: Request,
roleId: str = Path(..., description="Role ID"),
currentUser: User = Depends(requireSysAdmin)
@@ -334,7 +334,7 @@ async def delete_role(
@router.get("/users", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def list_users_with_roles(
+def list_users_with_roles(
request: Request,
roleLabel: Optional[str] = Query(None, description="Filter by role label"),
mandateId: Optional[str] = Query(None, description="Filter by mandate ID (via UserMandate)"),
@@ -396,7 +396,7 @@ async def list_users_with_roles(
@router.get("/users/{userId}", response_model=Dict[str, Any])
@limiter.limit("60/minute")
-async def get_user_roles(
+def get_user_roles(
request: Request,
userId: str = Path(..., description="User ID"),
currentUser: User = Depends(requireSysAdmin)
@@ -446,7 +446,7 @@ async def get_user_roles(
@router.put("/users/{userId}/roles", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def update_user_roles(
+def update_user_roles(
request: Request,
userId: str = Path(..., description="User ID"),
newRoleLabels: List[str] = Body(..., description="List of role labels to assign"),
@@ -540,7 +540,7 @@ async def update_user_roles(
@router.post("/users/{userId}/roles/{roleLabel}", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def add_user_role(
+def add_user_role(
request: Request,
userId: str = Path(..., description="User ID"),
roleLabel: str = Path(..., description="Role label to add"),
@@ -619,7 +619,7 @@ async def add_user_role(
@router.delete("/users/{userId}/roles/{roleLabel}", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def remove_user_role(
+def remove_user_role(
request: Request,
userId: str = Path(..., description="User ID"),
roleLabel: str = Path(..., description="Role label to remove"),
@@ -693,7 +693,7 @@ async def remove_user_role(
@router.get("/roles/{roleLabel}/users", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_users_with_role(
+def get_users_with_role(
request: Request,
roleLabel: str = Path(..., description="Role label"),
mandateId: Optional[str] = Query(None, description="Filter by mandate ID (via UserMandate)"),
diff --git a/modules/routes/routeAdminRbacRules.py b/modules/routes/routeAdminRbacRules.py
index 82cc13d7..1feb64a2 100644
--- a/modules/routes/routeAdminRbacRules.py
+++ b/modules/routes/routeAdminRbacRules.py
@@ -35,7 +35,7 @@ router = APIRouter(
@router.get("/permissions", response_model=UserPermissions)
@limiter.limit("300/minute") # Raised from 60 - sidebar checks many pages individually
-async def get_permissions(
+def get_permissions(
request: Request,
context: str = Query(..., description="Context type: DATA, UI, or RESOURCE"),
item: Optional[str] = Query(None, description="Item identifier (table name, UI path, or resource path)"),
@@ -101,7 +101,7 @@ async def get_permissions(
@router.get("/permissions/all", response_model=Dict[str, Any])
@limiter.limit("120/minute") # Raised from 30 - optimized endpoint for bulk permission fetch
-async def get_all_permissions(
+def get_all_permissions(
request: Request,
context: Optional[str] = Query(None, description="Context type: UI or RESOURCE (if not provided, returns both)"),
reqContext: RequestContext = Depends(getRequestContext)
@@ -293,7 +293,7 @@ async def get_all_permissions(
@router.get("/rules", response_model=PaginatedResponse)
@limiter.limit("30/minute")
-async def get_access_rules(
+def get_access_rules(
request: Request,
roleLabel: Optional[str] = Query(None, description="Filter by role label"),
context: Optional[str] = Query(None, description="Filter by context (DATA, UI, RESOURCE)"),
@@ -382,7 +382,7 @@ async def get_access_rules(
@router.get("/rules/by-role/{roleId}", response_model=PaginatedResponse)
@limiter.limit("30/minute")
-async def get_access_rules_by_role(
+def get_access_rules_by_role(
request: Request,
roleId: str = Path(..., description="Role ID to get rules for"),
currentUser: User = Depends(requireSysAdmin)
@@ -420,7 +420,7 @@ async def get_access_rules_by_role(
@router.get("/rules/{ruleId}", response_model=dict)
@limiter.limit("30/minute")
-async def get_access_rule(
+def get_access_rule(
request: Request,
ruleId: str = Path(..., description="Access rule ID"),
currentUser: User = Depends(requireSysAdmin)
@@ -462,7 +462,7 @@ async def get_access_rule(
@router.post("/rules", response_model=dict)
@limiter.limit("30/minute")
-async def create_access_rule(
+def create_access_rule(
request: Request,
accessRuleData: dict = Body(..., description="Access rule data"),
currentUser: User = Depends(requireSysAdmin)
@@ -528,7 +528,7 @@ async def create_access_rule(
@router.put("/rules/{ruleId}", response_model=dict)
@limiter.limit("30/minute")
-async def update_access_rule(
+def update_access_rule(
request: Request,
ruleId: str = Path(..., description="Access rule ID"),
accessRuleData: dict = Body(..., description="Updated access rule data"),
@@ -611,7 +611,7 @@ async def update_access_rule(
@router.delete("/rules/{ruleId}")
@limiter.limit("30/minute")
-async def delete_access_rule(
+def delete_access_rule(
request: Request,
ruleId: str = Path(..., description="Access rule ID"),
currentUser: User = Depends(requireSysAdmin)
@@ -669,7 +669,7 @@ async def delete_access_rule(
@router.get("/roles", response_model=PaginatedResponse)
@limiter.limit("60/minute")
-async def list_roles(
+def list_roles(
request: Request,
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
includeTemplates: bool = Query(False, description="Include feature template roles"),
@@ -838,7 +838,7 @@ async def list_roles(
@router.get("/roles/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_role_options(
+def get_role_options(
request: Request,
currentUser: User = Depends(requireSysAdmin)
) -> List[Dict[str, Any]]:
@@ -879,7 +879,7 @@ async def get_role_options(
@router.post("/roles", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def create_role(
+def create_role(
request: Request,
role: Role = Body(...),
currentUser: User = Depends(requireSysAdmin)
@@ -928,7 +928,7 @@ async def create_role(
@router.get("/roles/{roleId}", response_model=Dict[str, Any])
@limiter.limit("60/minute")
-async def get_role(
+def get_role(
request: Request,
roleId: str = Path(..., description="Role ID"),
currentUser: User = Depends(requireSysAdmin)
@@ -975,7 +975,7 @@ async def get_role(
@router.put("/roles/{roleId}", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def update_role(
+def update_role(
request: Request,
roleId: str = Path(..., description="Role ID"),
role: Role = Body(...),
@@ -1028,7 +1028,7 @@ async def update_role(
@router.delete("/roles/{roleId}", response_model=Dict[str, str])
@limiter.limit("30/minute")
-async def delete_role(
+def delete_role(
request: Request,
roleId: str = Path(..., description="Role ID"),
currentUser: User = Depends(requireSysAdmin)
@@ -1078,7 +1078,7 @@ async def delete_role(
@router.get("/catalog/objects", response_model=Dict[str, Any])
@limiter.limit("60/minute")
-async def getCatalogObjects(
+def getCatalogObjects(
request: Request,
context: Optional[str] = Query(None, description="Filter by context (DATA, UI, RESOURCE)"),
featureCode: Optional[str] = Query(None, description="Filter by feature code"),
@@ -1170,7 +1170,7 @@ async def getCatalogObjects(
@router.get("/catalog/stats", response_model=Dict[str, Any])
@limiter.limit("60/minute")
-async def getCatalogStats(
+def getCatalogStats(
request: Request,
currentUser: User = Depends(requireSysAdmin)
) -> Dict[str, Any]:
@@ -1200,7 +1200,7 @@ async def getCatalogStats(
@router.post("/cleanup/duplicate-rules", response_model=dict)
@limiter.limit("5/minute")
-async def cleanup_duplicate_access_rules(
+def cleanup_duplicate_access_rules(
request: Request,
dryRun: bool = Query(True, description="If true, only report duplicates without deleting"),
currentUser: User = Depends(requireSysAdmin)
diff --git a/modules/routes/routeAdminUserAccessOverview.py b/modules/routes/routeAdminUserAccessOverview.py
index 372e2193..b330d57e 100644
--- a/modules/routes/routeAdminUserAccessOverview.py
+++ b/modules/routes/routeAdminUserAccessOverview.py
@@ -69,7 +69,7 @@ def _getRoleScopePriority(scope: str) -> int:
@router.get("/users", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def listUsersForOverview(
+def listUsersForOverview(
request: Request,
currentUser: User = Depends(requireSysAdmin)
) -> List[Dict[str, Any]]:
@@ -112,7 +112,7 @@ async def listUsersForOverview(
@router.get("/{userId}", response_model=Dict[str, Any])
@limiter.limit("60/minute")
-async def getUserAccessOverview(
+def getUserAccessOverview(
request: Request,
userId: str = Path(..., description="User ID to get access overview for"),
mandateId: Optional[str] = Query(None, description="Filter by mandate ID"),
@@ -410,7 +410,7 @@ async def getUserAccessOverview(
@router.get("/{userId}/effective-permissions", response_model=Dict[str, Any])
@limiter.limit("60/minute")
-async def getEffectivePermissions(
+def getEffectivePermissions(
request: Request,
userId: str = Path(..., description="User ID"),
mandateId: str = Query(..., description="Mandate ID context"),
diff --git a/modules/routes/routeAttributes.py b/modules/routes/routeAttributes.py
index 10f93ce6..e877e512 100644
--- a/modules/routes/routeAttributes.py
+++ b/modules/routes/routeAttributes.py
@@ -22,7 +22,7 @@ router = APIRouter(
@router.get("/{entityType}", response_model=AttributeResponse)
@limiter.limit("30/minute")
-async def get_entity_attributes(
+def get_entity_attributes(
request: Request,
entityType: str = Path(..., description="Type of entity (e.g. prompt)")
) -> AttributeResponse:
@@ -76,7 +76,7 @@ async def get_entity_attributes(
@router.options("/{entityType}")
@limiter.limit("60/minute")
-async def options_entity_attributes(
+def options_entity_attributes(
request: Request,
entityType: str = Path(..., description="Type of entity (e.g. prompt)")
) -> Response:
diff --git a/modules/routes/routeBilling.py b/modules/routes/routeBilling.py
index bd47c791..26133704 100644
--- a/modules/routes/routeBilling.py
+++ b/modules/routes/routeBilling.py
@@ -164,7 +164,7 @@ router = APIRouter(
@router.get("/balance", response_model=List[BillingBalanceResponse])
@limiter.limit("60/minute")
-async def getBalance(
+def getBalance(
request: Request,
ctx: RequestContext = Depends(getRequestContext)
):
@@ -189,7 +189,7 @@ async def getBalance(
@router.get("/balance/{targetMandateId}", response_model=BillingBalanceResponse)
@limiter.limit("60/minute")
-async def getBalanceForMandate(
+def getBalanceForMandate(
request: Request,
targetMandateId: str = Path(..., description="Mandate ID"),
ctx: RequestContext = Depends(getRequestContext)
@@ -230,7 +230,7 @@ async def getBalanceForMandate(
@router.get("/transactions", response_model=List[TransactionResponse])
@limiter.limit("30/minute")
-async def getTransactions(
+def getTransactions(
request: Request,
limit: int = Query(default=50, ge=1, le=500),
offset: int = Query(default=0, ge=0),
@@ -276,7 +276,7 @@ async def getTransactions(
@router.get("/statistics/{period}", response_model=UsageReportResponse)
@limiter.limit("30/minute")
-async def getStatistics(
+def getStatistics(
request: Request,
period: str = Path(..., description="Period: 'day', 'month', or 'year'"),
year: int = Query(..., description="Year"),
@@ -361,7 +361,7 @@ async def getStatistics(
@router.get("/providers", response_model=List[str])
@limiter.limit("60/minute")
-async def getAllowedProviders(
+def getAllowedProviders(
request: Request,
ctx: RequestContext = Depends(getRequestContext)
):
@@ -388,7 +388,7 @@ async def getAllowedProviders(
@router.get("/admin/settings/{targetMandateId}", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def getSettingsAdmin(
+def getSettingsAdmin(
request: Request,
targetMandateId: str = Path(..., description="Mandate ID"),
ctx: RequestContext = Depends(getRequestContext),
@@ -415,7 +415,7 @@ async def getSettingsAdmin(
@router.post("/admin/settings/{targetMandateId}", response_model=Dict[str, Any])
@limiter.limit("10/minute")
-async def createOrUpdateSettings(
+def createOrUpdateSettings(
request: Request,
targetMandateId: str = Path(..., description="Mandate ID"),
settingsUpdate: BillingSettingsUpdate = Body(...),
@@ -462,7 +462,7 @@ async def createOrUpdateSettings(
@router.post("/admin/credit/{targetMandateId}", response_model=Dict[str, Any])
@limiter.limit("10/minute")
-async def addCredit(
+def addCredit(
request: Request,
targetMandateId: str = Path(..., description="Mandate ID"),
creditRequest: CreditAddRequest = Body(...),
@@ -526,7 +526,7 @@ async def addCredit(
@router.get("/admin/accounts/{targetMandateId}", response_model=List[AccountSummary])
@limiter.limit("30/minute")
-async def getAccounts(
+def getAccounts(
request: Request,
targetMandateId: str = Path(..., description="Mandate ID"),
ctx: RequestContext = Depends(getRequestContext),
@@ -572,7 +572,7 @@ class MandateUserSummary(BaseModel):
@router.get("/admin/users/{targetMandateId}", response_model=List[MandateUserSummary])
@limiter.limit("30/minute")
-async def getUsersForMandate(
+def getUsersForMandate(
request: Request,
targetMandateId: str = Path(..., description="Mandate ID"),
ctx: RequestContext = Depends(getRequestContext),
@@ -627,7 +627,7 @@ async def getUsersForMandate(
@router.get("/admin/transactions/{targetMandateId}", response_model=List[TransactionResponse])
@limiter.limit("30/minute")
-async def getTransactionsAdmin(
+def getTransactionsAdmin(
request: Request,
targetMandateId: str = Path(..., description="Mandate ID"),
limit: int = Query(default=100, ge=1, le=1000),
@@ -669,7 +669,7 @@ async def getTransactionsAdmin(
@router.get("/view/mandates/balances", response_model=List[MandateBalanceResponse])
@limiter.limit("30/minute")
-async def getMandateViewBalances(
+def getMandateViewBalances(
request: Request,
ctx: RequestContext = Depends(getRequestContext),
_admin = Depends(requireSysAdmin)
@@ -691,7 +691,7 @@ async def getMandateViewBalances(
@router.get("/view/mandates/transactions", response_model=List[TransactionResponse])
@limiter.limit("30/minute")
-async def getMandateViewTransactions(
+def getMandateViewTransactions(
request: Request,
limit: int = Query(default=100, ge=1, le=1000),
ctx: RequestContext = Depends(getRequestContext),
@@ -734,7 +734,7 @@ async def getMandateViewTransactions(
@router.get("/view/users/balances", response_model=List[UserBalanceResponse])
@limiter.limit("30/minute")
-async def getUserViewBalances(
+def getUserViewBalances(
request: Request,
ctx: RequestContext = Depends(getRequestContext)
):
@@ -793,7 +793,7 @@ class ViewStatisticsResponse(BaseModel):
@router.get("/view/statistics")
@limiter.limit("30/minute")
-async def getUserViewStatistics(
+def getUserViewStatistics(
request: Request,
period: str = Query(default="month", description="Period: 'day' or 'month'"),
year: int = Query(default=None, description="Year"),
@@ -962,7 +962,7 @@ async def getUserViewStatistics(
@router.get("/view/users/transactions", response_model=PaginatedResponse[UserTransactionResponse])
@limiter.limit("30/minute")
-async def getUserViewTransactions(
+def getUserViewTransactions(
request: Request,
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
ctx: RequestContext = Depends(getRequestContext)
diff --git a/modules/routes/routeDataConnections.py b/modules/routes/routeDataConnections.py
index 95bbd014..099b04c2 100644
--- a/modules/routes/routeDataConnections.py
+++ b/modules/routes/routeDataConnections.py
@@ -84,7 +84,7 @@ router = APIRouter(
@router.get("/statuses/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_connection_status_options(
+def get_connection_status_options(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> List[Dict[str, Any]]:
@@ -100,7 +100,7 @@ async def get_connection_status_options(
@router.get("/authorities/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_auth_authority_options(
+def get_auth_authority_options(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> List[Dict[str, Any]]:
@@ -288,7 +288,7 @@ async def get_connections(
@router.post("/", response_model=UserConnection)
@limiter.limit("10/minute")
-async def create_connection(
+def create_connection(
request: Request,
connection_data: Dict[str, Any] = Body(...),
currentUser: User = Depends(getCurrentUser)
@@ -344,7 +344,7 @@ async def create_connection(
@router.put("/{connectionId}", response_model=UserConnection)
@limiter.limit("10/minute")
-async def update_connection(
+def update_connection(
request: Request,
connectionId: str = Path(..., description="The ID of the connection to update"),
connection_data: Dict[str, Any] = Body(...),
@@ -416,7 +416,7 @@ async def update_connection(
@router.post("/{connectionId}/connect")
@limiter.limit("10/minute")
-async def connect_service(
+def connect_service(
request: Request,
connectionId: str = Path(..., description="The ID of the connection to connect"),
currentUser: User = Depends(getCurrentUser)
@@ -482,7 +482,7 @@ async def connect_service(
@router.post("/{connectionId}/disconnect")
@limiter.limit("10/minute")
-async def disconnect_service(
+def disconnect_service(
request: Request,
connectionId: str = Path(..., description="The ID of the connection to disconnect"),
currentUser: User = Depends(getCurrentUser)
@@ -532,7 +532,7 @@ async def disconnect_service(
@router.delete("/{connectionId}")
@limiter.limit("10/minute")
-async def delete_connection(
+def delete_connection(
request: Request,
connectionId: str = Path(..., description="The ID of the connection to delete"),
currentUser: User = Depends(getCurrentUser)
diff --git a/modules/routes/routeDataFiles.py b/modules/routes/routeDataFiles.py
index 1a84b7e4..49d7e365 100644
--- a/modules/routes/routeDataFiles.py
+++ b/modules/routes/routeDataFiles.py
@@ -37,7 +37,7 @@ router = APIRouter(
@router.get("/list", response_model=PaginatedResponse[FileItem])
@limiter.limit("30/minute")
-async def get_files(
+def get_files(
request: Request,
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
currentUser: User = Depends(getCurrentUser)
@@ -168,7 +168,7 @@ async def upload_file(
@router.get("/{fileId}", response_model=FileItem)
@limiter.limit("30/minute")
-async def get_file(
+def get_file(
request: Request,
fileId: str = Path(..., description="ID of the file"),
currentUser: User = Depends(getCurrentUser)
@@ -214,7 +214,7 @@ async def get_file(
@router.put("/{fileId}", response_model=FileItem)
@limiter.limit("10/minute")
-async def update_file(
+def update_file(
request: Request,
fileId: str = Path(..., description="ID of the file to update"),
file_info: Dict[str, Any] = Body(...),
@@ -262,7 +262,7 @@ async def update_file(
@router.delete("/{fileId}", response_model=Dict[str, Any])
@limiter.limit("10/minute")
-async def delete_file(
+def delete_file(
request: Request,
fileId: str = Path(..., description="ID of the file to delete"),
currentUser: User = Depends(getCurrentUser)
@@ -289,7 +289,7 @@ async def delete_file(
@router.get("/stats", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def get_file_stats(
+def get_file_stats(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
@@ -327,7 +327,7 @@ async def get_file_stats(
@router.get("/{fileId}/download")
@limiter.limit("30/minute")
-async def download_file(
+def download_file(
request: Request,
fileId: str = Path(..., description="ID of the file to download"),
currentUser: User = Depends(getCurrentUser)
@@ -375,7 +375,7 @@ async def download_file(
@router.get("/{fileId}/preview", response_model=FilePreview)
@limiter.limit("30/minute")
-async def preview_file(
+def preview_file(
request: Request,
fileId: str = Path(..., description="ID of the file to preview"),
currentUser: User = Depends(getCurrentUser)
diff --git a/modules/routes/routeDataMandates.py b/modules/routes/routeDataMandates.py
index 38877a9f..8d2c4a2b 100644
--- a/modules/routes/routeDataMandates.py
+++ b/modules/routes/routeDataMandates.py
@@ -76,7 +76,7 @@ router = APIRouter(
@router.get("/", response_model=PaginatedResponse[Mandate])
@limiter.limit("30/minute")
-async def get_mandates(
+def get_mandates(
request: Request,
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
currentUser: User = Depends(requireSysAdmin)
@@ -140,7 +140,7 @@ async def get_mandates(
@router.get("/{mandateId}", response_model=Mandate)
@limiter.limit("30/minute")
-async def get_mandate(
+def get_mandate(
request: Request,
mandateId: str = Path(..., description="ID of the mandate"),
currentUser: User = Depends(requireSysAdmin)
@@ -171,7 +171,7 @@ async def get_mandate(
@router.post("/", response_model=Mandate)
@limiter.limit("10/minute")
-async def create_mandate(
+def create_mandate(
request: Request,
mandateData: dict = Body(..., description="Mandate data with at least 'name' field"),
currentUser: User = Depends(requireSysAdmin)
@@ -224,7 +224,7 @@ async def create_mandate(
@router.put("/{mandateId}", response_model=Mandate)
@limiter.limit("10/minute")
-async def update_mandate(
+def update_mandate(
request: Request,
mandateId: str = Path(..., description="ID of the mandate to update"),
mandateData: dict = Body(..., description="Mandate update data"),
@@ -270,7 +270,7 @@ async def update_mandate(
@router.delete("/{mandateId}", response_model=Dict[str, Any])
@limiter.limit("10/minute")
-async def delete_mandate(
+def delete_mandate(
request: Request,
mandateId: str = Path(..., description="ID of the mandate to delete"),
currentUser: User = Depends(requireSysAdmin)
@@ -324,7 +324,7 @@ async def delete_mandate(
@router.get("/{targetMandateId}/users")
@limiter.limit("60/minute")
-async def list_mandate_users(
+def list_mandate_users(
request: Request,
targetMandateId: str = Path(..., description="ID of the mandate"),
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
@@ -493,7 +493,7 @@ async def list_mandate_users(
@router.post("/{targetMandateId}/users", response_model=UserMandateResponse)
@limiter.limit("30/minute")
-async def add_user_to_mandate(
+def add_user_to_mandate(
request: Request,
targetMandateId: str = Path(..., description="ID of the mandate"),
data: UserMandateCreate = Body(...),
@@ -603,7 +603,7 @@ async def add_user_to_mandate(
@router.delete("/{targetMandateId}/users/{targetUserId}", response_model=Dict[str, str])
@limiter.limit("30/minute")
-async def remove_user_from_mandate(
+def remove_user_from_mandate(
request: Request,
targetMandateId: str = Path(..., description="ID of the mandate"),
targetUserId: str = Path(..., description="ID of the user to remove"),
@@ -681,7 +681,7 @@ async def remove_user_from_mandate(
@router.put("/{targetMandateId}/users/{targetUserId}/roles", response_model=UserMandateResponse)
@limiter.limit("30/minute")
-async def update_user_roles_in_mandate(
+def update_user_roles_in_mandate(
request: Request,
targetMandateId: str = Path(..., description="ID of the mandate"),
targetUserId: str = Path(..., description="ID of the user"),
diff --git a/modules/routes/routeDataPrompts.py b/modules/routes/routeDataPrompts.py
index 48902e66..4aad221d 100644
--- a/modules/routes/routeDataPrompts.py
+++ b/modules/routes/routeDataPrompts.py
@@ -27,7 +27,7 @@ router = APIRouter(
@router.get("", response_model=PaginatedResponse[Prompt])
@limiter.limit("30/minute")
-async def get_prompts(
+def get_prompts(
request: Request,
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
currentUser: User = Depends(getCurrentUser)
@@ -83,7 +83,7 @@ async def get_prompts(
@router.post("", response_model=Prompt)
@limiter.limit("10/minute")
-async def create_prompt(
+def create_prompt(
request: Request,
prompt: Prompt,
currentUser: User = Depends(getCurrentUser)
@@ -98,7 +98,7 @@ async def create_prompt(
@router.get("/{promptId}", response_model=Prompt)
@limiter.limit("30/minute")
-async def get_prompt(
+def get_prompt(
request: Request,
promptId: str = Path(..., description="ID of the prompt"),
currentUser: User = Depends(getCurrentUser)
@@ -118,7 +118,7 @@ async def get_prompt(
@router.put("/{promptId}", response_model=Prompt)
@limiter.limit("10/minute")
-async def update_prompt(
+def update_prompt(
request: Request,
promptId: str = Path(..., description="ID of the prompt to update"),
promptData: Prompt = Body(...),
@@ -154,7 +154,7 @@ async def update_prompt(
@router.delete("/{promptId}", response_model=Dict[str, Any])
@limiter.limit("10/minute")
-async def delete_prompt(
+def delete_prompt(
request: Request,
promptId: str = Path(..., description="ID of the prompt to delete"),
currentUser: User = Depends(getCurrentUser)
diff --git a/modules/routes/routeDataUsers.py b/modules/routes/routeDataUsers.py
index 5e78d12a..b269e57e 100644
--- a/modules/routes/routeDataUsers.py
+++ b/modules/routes/routeDataUsers.py
@@ -153,7 +153,7 @@ router = APIRouter(
@router.get("/options", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def get_user_options(
+def get_user_options(
request: Request,
context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
@@ -190,7 +190,7 @@ async def get_user_options(
@router.get("/", response_model=PaginatedResponse[User])
@limiter.limit("30/minute")
-async def get_users(
+def get_users(
request: Request,
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
context: RequestContext = Depends(getRequestContext)
@@ -304,7 +304,7 @@ async def get_users(
@router.get("/{userId}", response_model=User)
@limiter.limit("30/minute")
-async def get_user(
+def get_user(
request: Request,
userId: str = Path(..., description="ID of the user"),
context: RequestContext = Depends(getRequestContext)
@@ -356,7 +356,7 @@ class CreateUserRequest(BaseModel):
@router.post("", response_model=User)
@limiter.limit("10/minute")
-async def create_user(
+def create_user(
request: Request,
userData: CreateUserRequest = Body(...),
context: RequestContext = Depends(getRequestContext)
@@ -396,7 +396,7 @@ async def create_user(
@router.put("/{userId}", response_model=User)
@limiter.limit("10/minute")
-async def update_user(
+def update_user(
request: Request,
userId: str = Path(..., description="ID of the user to update"),
userData: User = Body(...),
@@ -438,7 +438,7 @@ async def update_user(
@router.post("/{userId}/reset-password")
@limiter.limit("5/minute")
-async def reset_user_password(
+def reset_user_password(
request: Request,
userId: str = Path(..., description="ID of the user to reset password for"),
newPassword: str = Body(..., embed=True),
@@ -535,7 +535,7 @@ async def reset_user_password(
@router.post("/change-password")
@limiter.limit("5/minute")
-async def change_password(
+def change_password(
request: Request,
currentPassword: str = Body(..., embed=True),
newPassword: str = Body(..., embed=True),
@@ -614,7 +614,7 @@ async def change_password(
@router.post("/{userId}/send-password-link")
@limiter.limit("10/minute")
-async def send_password_link(
+def send_password_link(
request: Request,
userId: str = Path(..., description="ID of the user to send password setup link"),
frontendUrl: str = Body(..., embed=True),
@@ -749,7 +749,7 @@ Falls Sie diese Anforderung nicht erwartet haben, kontaktieren Sie bitte Ihren A
@router.delete("/{userId}", response_model=Dict[str, Any])
@limiter.limit("10/minute")
-async def delete_user(
+def delete_user(
request: Request,
userId: str = Path(..., description="ID of the user to delete"),
context: RequestContext = Depends(getRequestContext)
diff --git a/modules/routes/routeDataWorkflows.py b/modules/routes/routeDataWorkflows.py
index 80ca5986..88b41009 100644
--- a/modules/routes/routeDataWorkflows.py
+++ b/modules/routes/routeDataWorkflows.py
@@ -50,7 +50,7 @@ def getServiceChat(currentUser: User):
# Consolidated endpoint for getting all workflows
@router.get("/", response_model=PaginatedResponse[ChatWorkflow])
@limiter.limit("120/minute")
-async def get_workflows(
+def get_workflows(
request: Request,
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
currentUser: User = Depends(getCurrentUser)
@@ -123,7 +123,7 @@ async def get_workflows(
@router.get("/{workflowId}", response_model=ChatWorkflow)
@limiter.limit("120/minute")
-async def get_workflow(
+def get_workflow(
request: Request,
workflowId: str = Path(..., description="ID of the workflow"),
currentUser: User = Depends(getCurrentUser)
@@ -152,7 +152,7 @@ async def get_workflow(
@router.put("/{workflowId}", response_model=ChatWorkflow)
@limiter.limit("120/minute")
-async def update_workflow(
+def update_workflow(
request: Request,
workflowId: str = Path(..., description="ID of the workflow to update"),
workflowData: Dict[str, Any] = Body(...),
@@ -200,7 +200,7 @@ async def update_workflow(
# API Endpoint for workflow status
@router.get("/{workflowId}/status", response_model=ChatWorkflow)
@limiter.limit("120/minute")
-async def get_workflow_status(
+def get_workflow_status(
request: Request,
workflowId: str = Path(..., description="ID of the workflow"),
currentUser: User = Depends(getCurrentUser)
@@ -274,7 +274,7 @@ async def stop_workflow(
# API Endpoint for workflow logs with selective data transfer
@router.get("/{workflowId}/logs", response_model=PaginatedResponse[ChatLog])
@limiter.limit("120/minute")
-async def get_workflow_logs(
+def get_workflow_logs(
request: Request,
workflowId: str = Path(..., description="ID of the workflow"),
logId: Optional[str] = Query(None, description="Optional log ID to get only newer logs (legacy selective data transfer)"),
@@ -365,7 +365,7 @@ async def get_workflow_logs(
# API Endpoint for workflow messages with selective data transfer
@router.get("/{workflowId}/messages", response_model=PaginatedResponse[ChatMessage])
@limiter.limit("120/minute")
-async def get_workflow_messages(
+def get_workflow_messages(
request: Request,
workflowId: str = Path(..., description="ID of the workflow"),
messageId: Optional[str] = Query(None, description="Optional message ID to get only newer messages (legacy selective data transfer)"),
@@ -457,7 +457,7 @@ async def get_workflow_messages(
# State 11: Workflow Reset/Deletion endpoint
@router.delete("/{workflowId}", response_model=Dict[str, Any])
@limiter.limit("120/minute")
-async def delete_workflow(
+def delete_workflow(
request: Request,
workflowId: str = Path(..., description="ID of the workflow to delete"),
currentUser: User = Depends(getCurrentUser)
@@ -516,7 +516,7 @@ async def delete_workflow(
@router.delete("/{workflowId}/messages/{messageId}", response_model=Dict[str, Any])
@limiter.limit("120/minute")
-async def delete_workflow_message(
+def delete_workflow_message(
request: Request,
workflowId: str = Path(..., description="ID of the workflow"),
messageId: str = Path(..., description="ID of the message to delete"),
@@ -566,7 +566,7 @@ async def delete_workflow_message(
@router.delete("/{workflowId}/messages/{messageId}/files/{fileId}", response_model=Dict[str, Any])
@limiter.limit("120/minute")
-async def delete_file_from_message(
+def delete_file_from_message(
request: Request,
workflowId: str = Path(..., description="ID of the workflow"),
messageId: str = Path(..., description="ID of the message"),
@@ -615,7 +615,7 @@ async def delete_file_from_message(
@router.get("/actions", response_model=Dict[str, Any])
@limiter.limit("120/minute")
-async def get_all_actions(
+def get_all_actions(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
@@ -685,7 +685,7 @@ async def get_all_actions(
@router.get("/actions/{method}", response_model=Dict[str, Any])
@limiter.limit("120/minute")
-async def get_method_actions(
+def get_method_actions(
request: Request,
method: str = Path(..., description="Method name (e.g., 'outlook', 'sharepoint')"),
currentUser: User = Depends(getCurrentUser)
@@ -768,7 +768,7 @@ async def get_method_actions(
@router.get("/actions/{method}/{action}", response_model=Dict[str, Any])
@limiter.limit("120/minute")
-async def get_action_schema(
+def get_action_schema(
request: Request,
method: str = Path(..., description="Method name (e.g., 'outlook', 'sharepoint')"),
action: str = Path(..., description="Action name (e.g., 'readEmails', 'uploadDocument')"),
diff --git a/modules/routes/routeGdpr.py b/modules/routes/routeGdpr.py
index af0c7199..abc39b1f 100644
--- a/modules/routes/routeGdpr.py
+++ b/modules/routes/routeGdpr.py
@@ -74,7 +74,7 @@ class DeletionResult(BaseModel):
@router.get("/data-export", response_model=DataExportResponse)
@limiter.limit("5/minute")
-async def export_user_data(
+def export_user_data(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> DataExportResponse:
@@ -215,7 +215,7 @@ async def export_user_data(
@router.get("/data-portability")
@limiter.limit("5/minute")
-async def export_portable_data(
+def export_portable_data(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> JSONResponse:
@@ -296,7 +296,7 @@ async def export_portable_data(
@router.delete("/", response_model=DeletionResult)
@limiter.limit("1/hour")
-async def delete_account(
+def delete_account(
request: Request,
confirmDeletion: bool = False,
currentUser: User = Depends(getCurrentUser)
@@ -391,7 +391,7 @@ async def delete_account(
@router.get("/consent-info", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def get_consent_info(
+def get_consent_info(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
diff --git a/modules/routes/routeInvitations.py b/modules/routes/routeInvitations.py
index 6a53fb38..095b84fb 100644
--- a/modules/routes/routeInvitations.py
+++ b/modules/routes/routeInvitations.py
@@ -94,7 +94,7 @@ class InvitationValidation(BaseModel):
@router.post("/", response_model=InvitationResponse)
@limiter.limit("30/minute")
-async def create_invitation(
+def create_invitation(
request: Request,
data: InvitationCreate,
context: RequestContext = Depends(getRequestContext)
@@ -300,7 +300,7 @@ async def create_invitation(
@router.get("/", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def list_invitations(
+def list_invitations(
request: Request,
includeUsed: bool = Query(False, description="Include already used invitations"),
includeExpired: bool = Query(False, description="Include expired invitations"),
@@ -379,7 +379,7 @@ async def list_invitations(
@router.delete("/{invitationId}", response_model=Dict[str, str])
@limiter.limit("30/minute")
-async def revoke_invitation(
+def revoke_invitation(
request: Request,
invitationId: str,
context: RequestContext = Depends(getRequestContext)
@@ -458,7 +458,7 @@ async def revoke_invitation(
@router.get("/validate/{token}", response_model=InvitationValidation)
@limiter.limit("30/minute")
-async def validate_invitation(
+def validate_invitation(
request: Request,
token: str
) -> InvitationValidation:
@@ -562,7 +562,7 @@ async def validate_invitation(
@router.post("/accept/{token}", response_model=Dict[str, Any])
@limiter.limit("10/minute")
-async def accept_invitation(
+def accept_invitation(
request: Request,
token: str,
currentUser: User = Depends(getCurrentUser)
diff --git a/modules/routes/routeMessaging.py b/modules/routes/routeMessaging.py
index 419e9ae6..223181e0 100644
--- a/modules/routes/routeMessaging.py
+++ b/modules/routes/routeMessaging.py
@@ -38,7 +38,7 @@ router = APIRouter(
@router.get("/subscriptions", response_model=PaginatedResponse[MessagingSubscription])
@limiter.limit("60/minute")
-async def get_subscriptions(
+def get_subscriptions(
request: Request,
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
currentUser: User = Depends(getCurrentUser)
@@ -79,7 +79,7 @@ async def get_subscriptions(
@router.post("/subscriptions", response_model=MessagingSubscription)
@limiter.limit("60/minute")
-async def create_subscription(
+def create_subscription(
request: Request,
subscription: MessagingSubscription,
currentUser: User = Depends(getCurrentUser)
@@ -95,7 +95,7 @@ async def create_subscription(
@router.get("/subscriptions/{subscriptionId}", response_model=MessagingSubscription)
@limiter.limit("60/minute")
-async def get_subscription(
+def get_subscription(
request: Request,
subscriptionId: str = Path(..., description="ID of the subscription"),
currentUser: User = Depends(getCurrentUser)
@@ -115,7 +115,7 @@ async def get_subscription(
@router.put("/subscriptions/{subscriptionId}", response_model=MessagingSubscription)
@limiter.limit("60/minute")
-async def update_subscription(
+def update_subscription(
request: Request,
subscriptionId: str = Path(..., description="ID of the subscription to update"),
subscriptionData: MessagingSubscription = Body(...),
@@ -145,7 +145,7 @@ async def update_subscription(
@router.delete("/subscriptions/{subscriptionId}", response_model=Dict[str, Any])
@limiter.limit("60/minute")
-async def delete_subscription(
+def delete_subscription(
request: Request,
subscriptionId: str = Path(..., description="ID of the subscription to delete"),
currentUser: User = Depends(getCurrentUser)
@@ -174,7 +174,7 @@ async def delete_subscription(
@router.get("/subscriptions/{subscriptionId}/registrations", response_model=PaginatedResponse[MessagingSubscriptionRegistration])
@limiter.limit("60/minute")
-async def get_subscription_registrations(
+def get_subscription_registrations(
request: Request,
subscriptionId: str = Path(..., description="ID of the subscription"),
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
@@ -219,7 +219,7 @@ async def get_subscription_registrations(
@router.post("/subscriptions/{subscriptionId}/subscribe", response_model=MessagingSubscriptionRegistration)
@limiter.limit("60/minute")
-async def subscribe_user(
+def subscribe_user(
request: Request,
subscriptionId: str = Path(..., description="ID of the subscription"),
channel: MessagingChannel = Body(..., embed=True),
@@ -241,7 +241,7 @@ async def subscribe_user(
@router.delete("/subscriptions/{subscriptionId}/unsubscribe", response_model=Dict[str, Any])
@limiter.limit("60/minute")
-async def unsubscribe_user(
+def unsubscribe_user(
request: Request,
subscriptionId: str = Path(..., description="ID of the subscription"),
channel: MessagingChannel = Body(..., embed=True),
@@ -267,7 +267,7 @@ async def unsubscribe_user(
@router.get("/registrations", response_model=PaginatedResponse[MessagingSubscriptionRegistration])
@limiter.limit("60/minute")
-async def get_my_registrations(
+def get_my_registrations(
request: Request,
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
currentUser: User = Depends(getCurrentUser)
@@ -311,7 +311,7 @@ async def get_my_registrations(
@router.put("/registrations/{registrationId}", response_model=MessagingSubscriptionRegistration)
@limiter.limit("60/minute")
-async def update_registration(
+def update_registration(
request: Request,
registrationId: str = Path(..., description="ID of the registration to update"),
registrationData: MessagingSubscriptionRegistration = Body(...),
@@ -341,7 +341,7 @@ async def update_registration(
@router.delete("/registrations/{registrationId}", response_model=Dict[str, Any])
@limiter.limit("60/minute")
-async def delete_registration(
+def delete_registration(
request: Request,
registrationId: str = Path(..., description="ID of the registration to delete"),
currentUser: User = Depends(getCurrentUser)
@@ -376,7 +376,7 @@ def _getTriggerKey(request: Request) -> str:
@router.post("/trigger/{subscriptionId}", response_model=MessagingSubscriptionExecutionResult)
@limiter.limit("60/minute", key_func=_getTriggerKey)
-async def trigger_subscription(
+def trigger_subscription(
request: Request,
subscriptionId: str = Path(..., description="ID of the subscription to trigger"),
eventParameters: Dict[str, Any] = Body(...),
@@ -439,7 +439,7 @@ def _hasTriggerPermission(context: RequestContext) -> bool:
@router.get("/deliveries", response_model=PaginatedResponse[MessagingDelivery])
@limiter.limit("60/minute")
-async def get_deliveries(
+def get_deliveries(
request: Request,
subscriptionId: Optional[str] = Query(None, description="Filter by subscription ID"),
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
@@ -485,7 +485,7 @@ async def get_deliveries(
@router.get("/deliveries/{deliveryId}", response_model=MessagingDelivery)
@limiter.limit("60/minute")
-async def get_delivery(
+def get_delivery(
request: Request,
deliveryId: str = Path(..., description="ID of the delivery"),
currentUser: User = Depends(getCurrentUser)
diff --git a/modules/routes/routeNotifications.py b/modules/routes/routeNotifications.py
index 4fc09ac4..00e9bbcd 100644
--- a/modules/routes/routeNotifications.py
+++ b/modules/routes/routeNotifications.py
@@ -120,7 +120,7 @@ def createInvitationNotification(
@router.get("", response_model=List[Dict[str, Any]])
@limiter.limit("60/minute")
-async def getNotifications(
+def getNotifications(
request: Request,
currentUser: User = Depends(getCurrentUser),
status: Optional[str] = None,
@@ -161,7 +161,7 @@ async def getNotifications(
@router.get("/unread-count", response_model=UnreadCountResponse)
@limiter.limit("120/minute")
-async def getUnreadCount(
+def getUnreadCount(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> UnreadCountResponse:
@@ -190,7 +190,7 @@ async def getUnreadCount(
@router.put("/{notificationId}/read", response_model=Dict[str, Any])
@limiter.limit("60/minute")
-async def markAsRead(
+def markAsRead(
request: Request,
notificationId: str,
currentUser: User = Depends(getCurrentUser)
@@ -241,7 +241,7 @@ async def markAsRead(
@router.put("/mark-all-read", response_model=Dict[str, Any])
@limiter.limit("10/minute")
-async def markAllAsRead(
+def markAllAsRead(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
@@ -283,7 +283,7 @@ async def markAllAsRead(
@router.post("/{notificationId}/action", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def executeAction(
+def executeAction(
request: Request,
notificationId: str,
actionRequest: NotificationActionRequest,
@@ -332,7 +332,7 @@ async def executeAction(
actionResult = None
if notification.get("type") == NotificationType.INVITATION.value:
- actionResult = await _handleInvitationAction(
+ actionResult = _handleInvitationAction(
notification=notification,
actionId=actionRequest.actionId,
currentUser=currentUser,
@@ -370,7 +370,7 @@ async def executeAction(
)
-async def _handleInvitationAction(
+def _handleInvitationAction(
notification: Dict[str, Any],
actionId: str,
currentUser: User,
@@ -488,7 +488,7 @@ async def _handleInvitationAction(
@router.delete("/{notificationId}", response_model=Dict[str, Any])
@limiter.limit("30/minute")
-async def deleteNotification(
+def deleteNotification(
request: Request,
notificationId: str,
currentUser: User = Depends(getCurrentUser)
diff --git a/modules/routes/routeSecurityAdmin.py b/modules/routes/routeSecurityAdmin.py
index 75490eac..6a01cd9a 100644
--- a/modules/routes/routeSecurityAdmin.py
+++ b/modules/routes/routeSecurityAdmin.py
@@ -97,7 +97,7 @@ def _getDatabaseConnector(databaseName: str, userId: str = None) -> DatabaseConn
@router.get("/tokens")
@limiter.limit("30/minute")
-async def list_tokens(
+def list_tokens(
request: Request,
currentUser: User = Depends(requireSysAdmin),
userId: Optional[str] = None,
@@ -137,7 +137,7 @@ async def list_tokens(
@router.post("/tokens/revoke/user")
@limiter.limit("30/minute")
-async def revoke_tokens_by_user(
+def revoke_tokens_by_user(
request: Request,
currentUser: User = Depends(requireSysAdmin),
payload: Dict[str, Any] = Body(...)
@@ -172,7 +172,7 @@ async def revoke_tokens_by_user(
@router.post("/tokens/revoke/session")
@limiter.limit("30/minute")
-async def revoke_tokens_by_session(
+def revoke_tokens_by_session(
request: Request,
currentUser: User = Depends(requireSysAdmin),
payload: Dict[str, Any] = Body(...)
@@ -208,7 +208,7 @@ async def revoke_tokens_by_session(
@router.post("/tokens/revoke/id")
@limiter.limit("30/minute")
-async def revoke_token_by_id(
+def revoke_token_by_id(
request: Request,
currentUser: User = Depends(requireSysAdmin),
payload: Dict[str, Any] = Body(...)
@@ -235,7 +235,7 @@ async def revoke_token_by_id(
@router.post("/tokens/revoke/mandate")
@limiter.limit("10/minute")
-async def revoke_tokens_by_mandate(
+def revoke_tokens_by_mandate(
request: Request,
currentUser: User = Depends(requireSysAdmin),
payload: Dict[str, Any] = Body(...)
@@ -280,7 +280,7 @@ async def revoke_tokens_by_mandate(
@router.get("/logs/{log_name}")
@limiter.limit("60/minute")
-async def download_log(
+def download_log(
request: Request,
currentUser: User = Depends(requireSysAdmin),
log_name: str = "poweron"
@@ -309,7 +309,7 @@ async def download_log(
@router.get("/databases")
@limiter.limit("10/minute")
-async def list_databases(
+def list_databases(
request: Request,
currentUser: User = Depends(requireSysAdmin)
) -> Dict[str, Any]:
@@ -327,7 +327,7 @@ async def list_databases(
@router.get("/databases/{database_name}/tables")
@limiter.limit("30/minute")
-async def get_database_tables(
+def get_database_tables(
request: Request,
database_name: str,
currentUser: User = Depends(requireSysAdmin)
@@ -356,7 +356,7 @@ async def get_database_tables(
@router.post("/databases/{database_name}/tables/{table_name}/drop")
@limiter.limit("10/minute")
-async def drop_table(
+def drop_table(
request: Request,
database_name: str,
table_name: str,
@@ -404,7 +404,7 @@ async def drop_table(
@router.post("/databases/drop")
@limiter.limit("5/minute")
-async def drop_database(
+def drop_database(
request: Request,
currentUser: User = Depends(requireSysAdmin),
payload: Dict[str, Any] = Body(...)
diff --git a/modules/routes/routeSecurityGoogle.py b/modules/routes/routeSecurityGoogle.py
index 4ee634ed..cfaddc22 100644
--- a/modules/routes/routeSecurityGoogle.py
+++ b/modules/routes/routeSecurityGoogle.py
@@ -93,7 +93,7 @@ SCOPES = [
]
@router.get("/config")
-async def get_config():
+def get_config():
"""Debug endpoint to check Google OAuth configuration"""
return {
"client_id": CLIENT_ID,
@@ -109,7 +109,7 @@ async def get_config():
@router.get("/login")
@limiter.limit("5/minute")
-async def login(
+def login(
request: Request,
state: str = Query("login", description="State parameter to distinguish between login and connection flows"),
connectionId: Optional[str] = Query(None, description="Connection ID for connection flow")
@@ -589,7 +589,7 @@ async def auth_callback(code: str, state: str, request: Request, response: Respo
@router.get("/me", response_model=User)
@limiter.limit("30/minute")
-async def get_current_user(
+def get_current_user(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> User:
@@ -605,7 +605,7 @@ async def get_current_user(
@router.post("/logout")
@limiter.limit("10/minute")
-async def logout(
+def logout(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
diff --git a/modules/routes/routeSecurityLocal.py b/modules/routes/routeSecurityLocal.py
index 5f132833..46d00152 100644
--- a/modules/routes/routeSecurityLocal.py
+++ b/modules/routes/routeSecurityLocal.py
@@ -89,7 +89,7 @@ router = APIRouter(
@router.post("/login")
@limiter.limit("30/minute")
-async def login(
+def login(
request: Request,
response: Response,
formData: OAuth2PasswordRequestForm = Depends(),
@@ -242,7 +242,7 @@ async def login(
@router.post("/register")
@limiter.limit("10/minute")
-async def register_user(
+def register_user(
request: Request,
userData: User = Body(...),
frontendUrl: str = Body(..., embed=True)
@@ -381,7 +381,7 @@ Falls Sie sich nicht registriert haben, können Sie diese E-Mail ignorieren."""
@router.get("/me", response_model=User)
@limiter.limit("30/minute")
-async def read_user_me(
+def read_user_me(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> User:
@@ -397,7 +397,7 @@ async def read_user_me(
@router.post("/refresh")
@limiter.limit("60/minute")
-async def refresh_token(
+def refresh_token(
request: Request,
response: Response
) -> Dict[str, Any]:
@@ -472,7 +472,7 @@ async def refresh_token(
@router.post("/logout")
@limiter.limit("30/minute")
-async def logout(request: Request, response: Response, currentUser: User = Depends(getCurrentUser)) -> JSONResponse:
+def logout(request: Request, response: Response, currentUser: User = Depends(getCurrentUser)) -> JSONResponse:
"""Logout from local authentication"""
try:
# Get user interface with current user context
@@ -541,7 +541,7 @@ async def logout(request: Request, response: Response, currentUser: User = Depen
@router.get("/available")
@limiter.limit("10/minute")
-async def check_username_availability(
+def check_username_availability(
request: Request,
username: str,
authenticationAuthority: str = "local"
@@ -573,7 +573,7 @@ async def check_username_availability(
@router.post("/password-reset-request")
@limiter.limit("5/minute")
-async def password_reset_request(
+def password_reset_request(
request: Request,
username: str = Body(..., embed=True),
frontendUrl: str = Body(..., embed=True)
@@ -653,7 +653,7 @@ Falls Sie diese Anforderung nicht gestellt haben, können Sie diese E-Mail ignor
@router.post("/password-reset")
@limiter.limit("10/minute")
-async def password_reset(
+def password_reset(
request: Request,
token: str = Body(..., embed=True),
password: str = Body(..., embed=True)
diff --git a/modules/routes/routeSecurityMsft.py b/modules/routes/routeSecurityMsft.py
index 0abb2f56..338e2e33 100644
--- a/modules/routes/routeSecurityMsft.py
+++ b/modules/routes/routeSecurityMsft.py
@@ -66,7 +66,7 @@ SCOPES = [
@router.get("/login")
@limiter.limit("5/minute")
-async def login(
+def login(
request: Request,
state: str = Query("login", description="State parameter to distinguish between login and connection flows"),
connectionId: Optional[str] = Query(None, description="Connection ID for connection flow")
@@ -138,7 +138,7 @@ async def login(
@router.get("/adminconsent")
@limiter.limit("5/minute")
-async def adminconsent(request: Request) -> RedirectResponse:
+def adminconsent(request: Request) -> RedirectResponse:
"""Initiate Microsoft Admin Consent flow.
An Azure AD admin must visit this URL once to grant consent for the entire tenant.
@@ -161,7 +161,7 @@ async def adminconsent(request: Request) -> RedirectResponse:
)
@router.get("/adminconsent/callback")
-async def adminconsent_callback(
+def adminconsent_callback(
admin_consent: Optional[str] = Query(None),
tenant: Optional[str] = Query(None),
error: Optional[str] = Query(None),
@@ -603,7 +603,7 @@ async def auth_callback(code: str, state: str, request: Request, response: Respo
@router.get("/me", response_model=User)
@limiter.limit("30/minute")
-async def get_current_user(
+def get_current_user(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> User:
@@ -619,7 +619,7 @@ async def get_current_user(
@router.post("/logout")
@limiter.limit("10/minute")
-async def logout(
+def logout(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
@@ -655,7 +655,7 @@ async def logout(
@router.post("/cleanup")
@limiter.limit("5/minute")
-async def cleanup_expired_tokens(
+def cleanup_expired_tokens(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
diff --git a/modules/routes/routeSystem.py b/modules/routes/routeSystem.py
index 04e14063..3c8cdd3d 100644
--- a/modules/routes/routeSystem.py
+++ b/modules/routes/routeSystem.py
@@ -409,7 +409,7 @@ def _formatBlockItem(item: Dict[str, Any], language: str) -> Dict[str, Any]:
@navigationRouter.get("/navigation")
@limiter.limit("60/minute")
-async def get_navigation(
+def get_navigation(
request: Request,
language: str = Query("de", description="Language for labels (en, de, fr)"),
reqContext: RequestContext = Depends(getRequestContext)
diff --git a/modules/workflows/automation/mainWorkflow.py b/modules/workflows/automation/mainWorkflow.py
index e63f7932..172fc977 100644
--- a/modules/workflows/automation/mainWorkflow.py
+++ b/modules/workflows/automation/mainWorkflow.py
@@ -177,17 +177,14 @@ async def executeAutomation(automationId: str, services) -> ChatWorkflow:
workflow = services.interfaceDbChat.updateWorkflow(workflow.id, {"name": workflowName})
logger.info(f"Set workflow {workflow.id} name to: {workflowName}")
- # Update automation with execution log
+ # Save execution log (bypasses RBAC — system operation, not a user edit)
executionLogs = list(automation.executionLogs or [])
executionLogs.append(executionLog)
# Keep only last 50 executions
if len(executionLogs) > 50:
executionLogs = executionLogs[-50:]
- services.interfaceDbAutomation.updateAutomationDefinition(
- automationId,
- {"executionLogs": executionLogs}
- )
+ services.interfaceDbAutomation._saveExecutionLog(automationId, executionLogs)
return workflow
except Exception as e:
@@ -195,7 +192,7 @@ async def executeAutomation(automationId: str, services) -> ChatWorkflow:
executionLog["status"] = "error"
executionLog["messages"].append(f"Error: {str(e)}")
- # Update automation with execution log even on error
+ # Save execution log even on error (bypasses RBAC — system operation)
try:
automation = services.interfaceDbAutomation.getAutomationDefinition(automationId)
if automation:
@@ -203,10 +200,7 @@ async def executeAutomation(automationId: str, services) -> ChatWorkflow:
executionLogs.append(executionLog)
if len(executionLogs) > 50:
executionLogs = executionLogs[-50:]
- services.interfaceDbAutomation.updateAutomationDefinition(
- automationId,
- {"executionLogs": executionLogs}
- )
+ services.interfaceDbAutomation._saveExecutionLog(automationId, executionLogs)
except Exception as logError:
logger.error(f"Error saving execution log: {str(logError)}")
diff --git a/scripts/migrate_async_to_sync.py b/scripts/migrate_async_to_sync.py
new file mode 100644
index 00000000..d0f8ef67
--- /dev/null
+++ b/scripts/migrate_async_to_sync.py
@@ -0,0 +1,377 @@
+#!/usr/bin/env python3
+"""
+Migration Script: Convert async def → def for route handlers that don't need async.
+
+This fixes the event-loop blocking issue where synchronous psycopg2 DB operations
+inside async def routes block the entire uvicorn event loop, preventing concurrent
+request handling.
+
+FastAPI behavior:
+- `async def` routes → run directly on the event loop (blocks if sync code inside)
+- `def` routes → run in a thread pool automatically (non-blocking)
+
+Usage:
+ python scripts/migrate_async_to_sync.py --dry-run # Preview changes
+ python scripts/migrate_async_to_sync.py # Apply changes
+
+Author: Auto-generated migration script
+"""
+
+import os
+import re
+import sys
+import argparse
+from pathlib import Path
+from typing import Dict, List, Set, Tuple
+
+# Base directory
+GATEWAY_DIR = Path(__file__).parent.parent
+ROUTES_DIR = GATEWAY_DIR / "modules" / "routes"
+FEATURES_DIR = GATEWAY_DIR / "modules" / "features"
+AUTH_DIR = GATEWAY_DIR / "modules" / "auth"
+
+
+# =============================================================================
+# Configuration: Functions that MUST stay async
+# =============================================================================
+
+# Key: relative file path from gateway dir
+# Value: set of function names that must remain async def
+_MUST_STAY_ASYNC: Dict[str, Set[str]] = {
+ # --- routes/ ---
+ "modules/routes/routeAdminAutomationEvents.py": {
+ "sync_all_automation_events", # await syncAutomationEvents(...)
+ },
+ "modules/routes/routeAdminRbacExport.py": {
+ "import_global_rbac", # await file.read()
+ "import_mandate_rbac", # await file.read()
+ },
+ "modules/routes/routeDataConnections.py": {
+ "get_connections", # await token_refresh_service.refresh_expired_tokens(...)
+ },
+ "modules/routes/routeDataFiles.py": {
+ "upload_file", # await file.read()
+ },
+ "modules/routes/routeDataWorkflows.py": {
+ "stop_workflow", # await chatStop(...)
+ },
+ # These files have many genuinely async routes (httpx, external APIs) -- keep ALL async:
+ "modules/routes/routeRealEstate.py": "__ALL__",
+ "modules/routes/routeSharepoint.py": "__ALL__",
+ "modules/routes/routeVoiceGoogle.py": "__ALL__",
+ # Partial keeps in security routes (httpx.AsyncClient, request.json()):
+ "modules/routes/routeSecurityGoogle.py": {
+ "verify_google_token", # await client.get(...)
+ "auth_callback", # await verify_google_token(...), await client.get(...)
+ "verify_token", # await verify_google_token(...)
+ "refresh_token", # await request.json()
+ },
+ "modules/routes/routeSecurityMsft.py": {
+ "auth_callback", # await client.get(...)
+ "refresh_token", # await request.json()
+ },
+ # --- features/ ---
+ "modules/features/automation/routeFeatureAutomation.py": {
+ "execute_automation_route", # await executeAutomation(...)
+ },
+ "modules/features/chatbot/routeFeatureChatbot.py": {
+ "stream_chatbot_start", # await chatProcess(...), contains async event_stream generator
+ "event_stream", # await request.is_disconnected(), await asyncio.wait_for(...)
+ "stop_chatbot", # await event_manager.emit_event(...)
+ },
+ "modules/features/chatplayground/routeFeatureChatplayground.py": {
+ "start_workflow", # await chatStart(...)
+ "stop_workflow", # await chatStop(...)
+ },
+ "modules/features/neutralization/routeFeatureNeutralizer.py": {
+ "process_sharepoint_files", # await service.processSharepointFiles(...)
+ },
+ "modules/features/realestate/routeFeatureRealEstate.py": {
+ "process_command", # await processNaturalLanguageCommand(...)
+ "create_table_record", # await create_project_with_parcel_data(...)
+ "search_parcel", # await connector.search_parcel(...), connector._query_building_layer(...)
+ "add_parcel_to_project", # await connector.search_parcel(...)
+ },
+ "modules/features/trustee/routeFeatureTrustee.py": {
+ "create_document", # await request.json()
+ "upload_document", # await file.read()
+ },
+}
+
+# Files to skip entirely (all routes must stay async)
+_SKIP_FILES: Set[str] = {
+ "modules/routes/routeRealEstate.py",
+ "modules/routes/routeSharepoint.py",
+ "modules/routes/routeVoiceGoogle.py",
+}
+
+# Helper functions that are fake-async (async def but no await inside)
+# These will be converted from async def -> def
+_FAKE_ASYNC_HELPERS: Dict[str, Set[str]] = {
+ "modules/features/chatplayground/routeFeatureChatplayground.py": {"_validateInstanceAccess"},
+ "modules/features/trustee/routeFeatureTrustee.py": {"_validateInstanceAccess", "_validateInstanceAdmin"},
+ "modules/features/realestate/routeFeatureRealEstate.py": {"_validateInstanceAccess"},
+ "modules/features/chatbot/routeFeatureChatbot.py": {"_validateInstanceAccess"},
+ "modules/routes/routeNotifications.py": {"_handleInvitationAction"},
+}
+
+# Calls to these functions should have 'await' removed after they become sync
+_REMOVE_AWAIT_CALLS: Set[str] = {
+ "_validateInstanceAccess",
+ "_validateInstanceAdmin",
+ "_handleInvitationAction",
+}
+
+
+# =============================================================================
+# Migration Logic
+# =============================================================================
+
+def _getRelativePath(filePath: Path) -> str:
+ """Get path relative to gateway dir."""
+ try:
+ return str(filePath.relative_to(GATEWAY_DIR)).replace("\\", "/")
+ except ValueError:
+ return str(filePath)
+
+
+def _shouldSkipFile(relPath: str) -> bool:
+ """Check if file should be skipped entirely."""
+ return relPath in _SKIP_FILES or _MUST_STAY_ASYNC.get(relPath) == "__ALL__"
+
+
+def _mustStayAsync(relPath: str, funcName: str) -> bool:
+ """Check if a specific function must stay async."""
+ keepSet = _MUST_STAY_ASYNC.get(relPath, set())
+ if keepSet == "__ALL__":
+ return True
+ return funcName in keepSet
+
+
+def _isFakeAsyncHelper(relPath: str, funcName: str) -> bool:
+ """Check if a function is a fake-async helper that should be converted."""
+ helpers = _FAKE_ASYNC_HELPERS.get(relPath, set())
+ return funcName in helpers
+
+
+def _processFile(filePath: Path, dryRun: bool = True) -> Dict[str, any]:
+ """Process a single file and convert async def → def where appropriate."""
+ relPath = _getRelativePath(filePath)
+
+ if _shouldSkipFile(relPath):
+ return {"file": relPath, "skipped": True, "reason": "all routes must stay async"}
+
+ with open(filePath, "r", encoding="utf-8") as f:
+ originalContent = f.read()
+
+ content = originalContent
+ changes = []
+
+ # Step 1: Find all async def functions and convert eligible ones
+ # Pattern matches: async def function_name(
+ asyncDefPattern = re.compile(r'^(\s*)async def (\w+)\s*\(', re.MULTILINE)
+
+ convertedFunctions = set()
+
+ for match in asyncDefPattern.finditer(originalContent):
+ indent = match.group(1)
+ funcName = match.group(2)
+
+ # Check if this function must stay async
+ if _mustStayAsync(relPath, funcName):
+ changes.append(f" KEEP async: {funcName} (must stay async)")
+ continue
+
+ # Convert async def → def
+ convertedFunctions.add(funcName)
+ changes.append(f" CONVERT: async def {funcName} -> def {funcName}")
+
+ # Apply conversions
+ for funcName in convertedFunctions:
+ # Replace "async def funcName(" with "def funcName("
+ # Be careful to match the exact function definition
+ pattern = re.compile(
+ r'^(\s*)async def ' + re.escape(funcName) + r'\s*\(',
+ re.MULTILINE
+ )
+ content = pattern.sub(
+ lambda m: f'{m.group(1)}def {funcName}(',
+ content
+ )
+
+ # Step 2: Remove 'await' from calls to converted functions
+ # This handles: await _validateInstanceAccess(...) → _validateInstanceAccess(...)
+ # And also: result = await someConvertedFunc(...) → result = someConvertedFunc(...)
+ for funcName in _REMOVE_AWAIT_CALLS:
+ if funcName in convertedFunctions or _isFakeAsyncHelper(relPath, funcName):
+ awaitPattern = re.compile(
+ r'(\s*)(.*)await\s+' + re.escape(funcName) + r'\s*\(',
+ re.MULTILINE
+ )
+ newContent = awaitPattern.sub(
+ lambda m: f'{m.group(1)}{m.group(2)}{funcName}(',
+ content
+ )
+ if newContent != content:
+ changes.append(f" REMOVE await: await {funcName}(...) -> {funcName}(...)")
+ content = newContent
+
+ # Step 3: Check for any remaining 'await' in converted functions
+ # This catches cases where a converted function still has await calls
+ remainingAwaits = []
+ lines = content.split('\n')
+ currentFunc = None
+ funcIndent = 0
+
+ for i, line in enumerate(lines):
+ # Track current function
+ defMatch = re.match(r'^(\s*)def (\w+)\s*\(', line)
+ asyncDefMatch = re.match(r'^(\s*)async def (\w+)\s*\(', line)
+
+ if defMatch and defMatch.group(2) in convertedFunctions:
+ currentFunc = defMatch.group(2)
+ funcIndent = len(defMatch.group(1))
+ elif defMatch or asyncDefMatch:
+ currentFunc = None
+ elif currentFunc and line.strip() and not line[0].isspace():
+ currentFunc = None
+
+ # Check for remaining awaits in converted functions
+ if currentFunc and 'await ' in line:
+ remainingAwaits.append(f" WARNING: Remaining 'await' in {currentFunc} at line {i+1}: {line.strip()}")
+
+ # Build result
+ result = {
+ "file": relPath,
+ "skipped": False,
+ "convertedCount": len(convertedFunctions),
+ "convertedFunctions": sorted(convertedFunctions),
+ "changes": changes,
+ "warnings": remainingAwaits,
+ "modified": content != originalContent,
+ }
+
+ # Write file if not dry run and content changed
+ if not dryRun and content != originalContent:
+ with open(filePath, "w", encoding="utf-8") as f:
+ f.write(content)
+ result["written"] = True
+ else:
+ result["written"] = False
+
+ return result
+
+
+def _discoverRouteFiles() -> List[Path]:
+ """Discover all route files to process."""
+ files = []
+
+ # Standard routes
+ if ROUTES_DIR.exists():
+ for f in sorted(ROUTES_DIR.glob("route*.py")):
+ files.append(f)
+
+ # Feature routes
+ if FEATURES_DIR.exists():
+ for f in sorted(FEATURES_DIR.glob("*/routeFeature*.py")):
+ files.append(f)
+
+ return files
+
+
+def _main():
+ parser = argparse.ArgumentParser(
+ description="Migrate async def → def for FastAPI routes with sync DB operations"
+ )
+ parser.add_argument(
+ "--dry-run",
+ action="store_true",
+ default=False,
+ help="Preview changes without writing files (default: apply changes)"
+ )
+ parser.add_argument(
+ "--file",
+ type=str,
+ default=None,
+ help="Process only a specific file (relative to gateway dir)"
+ )
+ args = parser.parse_args()
+
+ dryRun = args.dry_run
+
+ print("=" * 70)
+ print(f" FastAPI Route Migration: async def -> def")
+ print(f" Mode: {'DRY RUN (preview only)' if dryRun else 'APPLY CHANGES'}")
+ print("=" * 70)
+ print()
+
+ # Discover files
+ if args.file:
+ targetFile = GATEWAY_DIR / args.file.replace("/", os.sep)
+ if not targetFile.exists():
+ print(f"ERROR: File not found: {targetFile}")
+ sys.exit(1)
+ files = [targetFile]
+ else:
+ files = _discoverRouteFiles()
+
+ print(f"Found {len(files)} route files to analyze\n")
+
+ totalConverted = 0
+ totalWarnings = 0
+ totalModified = 0
+ allResults = []
+
+ for filePath in files:
+ result = _processFile(filePath, dryRun=dryRun)
+ allResults.append(result)
+
+ if result.get("skipped"):
+ print(f"[SKIP] {result['file']} - SKIPPED ({result.get('reason', '')})")
+ continue
+
+ converted = result.get("convertedCount", 0)
+ warnings = result.get("warnings", [])
+ modified = result.get("modified", False)
+
+ if converted == 0 and not warnings:
+ continue
+
+ totalConverted += converted
+ totalWarnings += len(warnings)
+ if modified:
+ totalModified += 1
+
+ status = "[DONE] WRITTEN" if result.get("written") else ("[PLAN] WOULD WRITE" if modified else "---")
+ print(f"{status} {result['file']} ({converted} functions)")
+
+ for change in result.get("changes", []):
+ print(f" {change}")
+
+ for warning in warnings:
+ print(f" [WARN] {warning}")
+
+ print()
+
+ # Summary
+ print("=" * 70)
+ print(f" SUMMARY")
+ print(f" Files analyzed: {len(files)}")
+ print(f" Files modified: {totalModified}")
+ print(f" Functions converted: {totalConverted}")
+ print(f" Warnings: {totalWarnings}")
+ if dryRun:
+ print(f"\n This was a DRY RUN. Run without --dry-run to apply changes.")
+ else:
+ print(f"\n Changes applied. Restart the server to take effect.")
+ print("=" * 70)
+
+ # Return exit code based on warnings
+ if totalWarnings > 0:
+ print(f"\n[WARN] There are {totalWarnings} warnings - review before deploying!")
+ return 1
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(_main())