fiixed feature instance role access
This commit is contained in:
parent
f15ed2e380
commit
8d28f6d77b
33 changed files with 764 additions and 376 deletions
|
|
@ -88,7 +88,9 @@ class AutomationObjects:
|
||||||
permissions = self.rbac.getUserPermissions(
|
permissions = self.rbac.getUserPermissions(
|
||||||
user=self.currentUser,
|
user=self.currentUser,
|
||||||
context=AccessRuleContext.DATA,
|
context=AccessRuleContext.DATA,
|
||||||
item=objectKey
|
item=objectKey,
|
||||||
|
mandateId=self.mandateId,
|
||||||
|
featureInstanceId=self.featureInstanceId
|
||||||
)
|
)
|
||||||
|
|
||||||
accessLevel = getattr(permissions, action, AccessLevel.NONE)
|
accessLevel = getattr(permissions, action, AccessLevel.NONE)
|
||||||
|
|
@ -373,6 +375,21 @@ class AutomationObjects:
|
||||||
logger.error(f"Error creating automation definition: {str(e)}")
|
logger.error(f"Error creating automation definition: {str(e)}")
|
||||||
raise
|
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:
|
def updateAutomationDefinition(self, automationId: str, automationData: Dict[str, Any]) -> AutomationDefinition:
|
||||||
"""Updates an automation definition, then triggers sync."""
|
"""Updates an automation definition, then triggers sync."""
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.get("", response_model=PaginatedResponse[AutomationDefinition])
|
@router.get("", response_model=PaginatedResponse[AutomationDefinition])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_automations(
|
def get_automations(
|
||||||
request: Request,
|
request: Request,
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -107,7 +107,7 @@ async def get_automations(
|
||||||
|
|
||||||
@router.post("", response_model=AutomationDefinition)
|
@router.post("", response_model=AutomationDefinition)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_automation(
|
def create_automation(
|
||||||
request: Request,
|
request: Request,
|
||||||
automation: AutomationDefinition,
|
automation: AutomationDefinition,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -128,7 +128,7 @@ async def create_automation(
|
||||||
)
|
)
|
||||||
|
|
||||||
@router.get("/attributes", response_model=Dict[str, Any])
|
@router.get("/attributes", response_model=Dict[str, Any])
|
||||||
async def get_automation_attributes(
|
def get_automation_attributes(
|
||||||
request: Request
|
request: Request
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Get attribute definitions for AutomationDefinition model"""
|
"""Get attribute definitions for AutomationDefinition model"""
|
||||||
|
|
@ -137,7 +137,7 @@ async def get_automation_attributes(
|
||||||
|
|
||||||
@router.get("/actions")
|
@router.get("/actions")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_available_actions(
|
def get_available_actions(
|
||||||
request: Request,
|
request: Request,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> JSONResponse:
|
) -> JSONResponse:
|
||||||
|
|
@ -230,7 +230,7 @@ async def get_available_actions(
|
||||||
|
|
||||||
@router.get("/{automationId}", response_model=AutomationDefinition)
|
@router.get("/{automationId}", response_model=AutomationDefinition)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_automation(
|
def get_automation(
|
||||||
request: Request,
|
request: Request,
|
||||||
automationId: str = Path(..., description="Automation ID"),
|
automationId: str = Path(..., description="Automation ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -257,7 +257,7 @@ async def get_automation(
|
||||||
|
|
||||||
@router.put("/{automationId}", response_model=AutomationDefinition)
|
@router.put("/{automationId}", response_model=AutomationDefinition)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_automation(
|
def update_automation(
|
||||||
request: Request,
|
request: Request,
|
||||||
automationId: str = Path(..., description="Automation ID"),
|
automationId: str = Path(..., description="Automation ID"),
|
||||||
automation: AutomationDefinition = Body(...),
|
automation: AutomationDefinition = Body(...),
|
||||||
|
|
@ -285,7 +285,7 @@ async def update_automation(
|
||||||
|
|
||||||
@router.patch("/{automationId}/status")
|
@router.patch("/{automationId}/status")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def update_automation_status(
|
def update_automation_status(
|
||||||
request: Request,
|
request: Request,
|
||||||
automationId: str = Path(..., description="Automation ID"),
|
automationId: str = Path(..., description="Automation ID"),
|
||||||
active: bool = Body(..., embed=True),
|
active: bool = Body(..., embed=True),
|
||||||
|
|
@ -326,7 +326,7 @@ async def update_automation_status(
|
||||||
|
|
||||||
@router.delete("/{automationId}")
|
@router.delete("/{automationId}")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_automation(
|
def delete_automation(
|
||||||
request: Request,
|
request: Request,
|
||||||
automationId: str = Path(..., description="Automation ID"),
|
automationId: str = Path(..., description="Automation ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -407,7 +407,7 @@ templateAttributes = getModelAttributeDefinitions(AutomationTemplate)
|
||||||
|
|
||||||
@templateRouter.get("", response_model=PaginatedResponse[AutomationTemplate])
|
@templateRouter.get("", response_model=PaginatedResponse[AutomationTemplate])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_db_templates(
|
def get_db_templates(
|
||||||
request: Request,
|
request: Request,
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -470,7 +470,7 @@ async def get_db_templates(
|
||||||
|
|
||||||
|
|
||||||
@templateRouter.get("/attributes", response_model=Dict[str, Any])
|
@templateRouter.get("/attributes", response_model=Dict[str, Any])
|
||||||
async def get_template_attributes(
|
def get_template_attributes(
|
||||||
request: Request
|
request: Request
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Get attribute definitions for AutomationTemplate model"""
|
"""Get attribute definitions for AutomationTemplate model"""
|
||||||
|
|
@ -479,7 +479,7 @@ async def get_template_attributes(
|
||||||
|
|
||||||
@templateRouter.get("/{templateId}")
|
@templateRouter.get("/{templateId}")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_db_template(
|
def get_db_template(
|
||||||
request: Request,
|
request: Request,
|
||||||
templateId: str = Path(..., description="Template ID"),
|
templateId: str = Path(..., description="Template ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -511,7 +511,7 @@ async def get_db_template(
|
||||||
|
|
||||||
@templateRouter.post("")
|
@templateRouter.post("")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_db_template(
|
def create_db_template(
|
||||||
request: Request,
|
request: Request,
|
||||||
templateData: Dict[str, Any] = Body(...),
|
templateData: Dict[str, Any] = Body(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -542,7 +542,7 @@ async def create_db_template(
|
||||||
|
|
||||||
@templateRouter.put("/{templateId}")
|
@templateRouter.put("/{templateId}")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_db_template(
|
def update_db_template(
|
||||||
request: Request,
|
request: Request,
|
||||||
templateId: str = Path(..., description="Template ID"),
|
templateId: str = Path(..., description="Template ID"),
|
||||||
templateData: Dict[str, Any] = Body(...),
|
templateData: Dict[str, Any] = Body(...),
|
||||||
|
|
@ -574,7 +574,7 @@ async def update_db_template(
|
||||||
|
|
||||||
@templateRouter.delete("/{templateId}")
|
@templateRouter.delete("/{templateId}")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_db_template(
|
def delete_db_template(
|
||||||
request: Request,
|
request: Request,
|
||||||
templateId: str = Path(..., description="Template ID"),
|
templateId: str = Path(..., description="Template ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
|
||||||
|
|
@ -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.
|
Validate that the user has access to the feature instance.
|
||||||
Returns the mandateId for the instance.
|
Returns the mandateId for the instance.
|
||||||
|
|
@ -124,7 +124,7 @@ async def stream_chatbot_start(
|
||||||
- Query parameter takes precedence if both are provided
|
- Query parameter takes precedence if both are provided
|
||||||
"""
|
"""
|
||||||
# Validate instance access
|
# Validate instance access
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
event_manager = get_event_manager()
|
event_manager = get_event_manager()
|
||||||
|
|
||||||
|
|
@ -323,7 +323,7 @@ async def stop_chatbot(
|
||||||
) -> ChatWorkflow:
|
) -> ChatWorkflow:
|
||||||
"""Stops a running chatbot workflow."""
|
"""Stops a running chatbot workflow."""
|
||||||
# Validate instance access
|
# Validate instance access
|
||||||
await _validateInstanceAccess(instanceId, context)
|
_validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get chatbot interface with instance context
|
# Get chatbot interface with instance context
|
||||||
|
|
@ -392,7 +392,7 @@ async def stop_chatbot(
|
||||||
# to prevent "threads" from being matched as a workflowId
|
# to prevent "threads" from being matched as a workflowId
|
||||||
@router.get("/{instanceId}/threads")
|
@router.get("/{instanceId}/threads")
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def get_chatbot_threads(
|
def get_chatbot_threads(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
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"),
|
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
|
- If workflowId is not provided: Returns a paginated list of all workflows
|
||||||
"""
|
"""
|
||||||
# Validate instance access
|
# Validate instance access
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
interfaceDbChat = _getServiceChat(context, instanceId)
|
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
|
# NOTE: This catch-all route MUST be defined AFTER more specific routes like /threads
|
||||||
@router.delete("/{instanceId}/{workflowId}", response_model=Dict[str, Any])
|
@router.delete("/{instanceId}/{workflowId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def delete_chatbot(
|
def delete_chatbot(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
workflowId: str = Path(..., description="ID of the workflow to delete"),
|
workflowId: str = Path(..., description="ID of the workflow to delete"),
|
||||||
|
|
@ -531,7 +531,7 @@ async def delete_chatbot(
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Deletes a chatbot workflow and its associated data."""
|
"""Deletes a chatbot workflow and its associated data."""
|
||||||
# Validate instance access - if user has access to instance, they can delete their workflows
|
# Validate instance access - if user has access to instance, they can delete their workflows
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get service center
|
# Get service center
|
||||||
|
|
|
||||||
|
|
@ -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.
|
Validate that user has access to the feature instance.
|
||||||
|
|
||||||
|
|
@ -93,7 +93,7 @@ async def start_workflow(
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Validate access and get mandate ID
|
# Validate access and get mandate ID
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
# Start or continue workflow
|
# Start or continue workflow
|
||||||
workflow = await chatStart(
|
workflow = await chatStart(
|
||||||
|
|
@ -129,7 +129,7 @@ async def stop_workflow(
|
||||||
"""Stops a running workflow."""
|
"""Stops a running workflow."""
|
||||||
try:
|
try:
|
||||||
# Validate access and get mandate ID
|
# Validate access and get mandate ID
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
# Stop workflow (pass featureInstanceId for proper RBAC filtering)
|
# Stop workflow (pass featureInstanceId for proper RBAC filtering)
|
||||||
workflow = await chatStop(
|
workflow = await chatStop(
|
||||||
|
|
@ -154,7 +154,7 @@ async def stop_workflow(
|
||||||
# Unified Chat Data Endpoint for Polling
|
# Unified Chat Data Endpoint for Polling
|
||||||
@router.get("/{instanceId}/{workflowId}/chatData")
|
@router.get("/{instanceId}/{workflowId}/chatData")
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def get_workflow_chat_data(
|
def get_workflow_chat_data(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature instance ID"),
|
instanceId: str = Path(..., description="Feature instance ID"),
|
||||||
workflowId: str = Path(..., description="ID of the workflow"),
|
workflowId: str = Path(..., description="ID of the workflow"),
|
||||||
|
|
@ -167,7 +167,7 @@ async def get_workflow_chat_data(
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Validate access
|
# Validate access
|
||||||
await _validateInstanceAccess(instanceId, context)
|
_validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
# Get service with feature instance context
|
# Get service with feature instance context
|
||||||
chatInterface = _getServiceChat(context, featureInstanceId=instanceId)
|
chatInterface = _getServiceChat(context, featureInstanceId=instanceId)
|
||||||
|
|
@ -198,7 +198,7 @@ async def get_workflow_chat_data(
|
||||||
# Get workflows for this instance
|
# Get workflows for this instance
|
||||||
@router.get("/{instanceId}/workflows")
|
@router.get("/{instanceId}/workflows")
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def get_workflows(
|
def get_workflows(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature instance ID"),
|
instanceId: str = Path(..., description="Feature instance ID"),
|
||||||
page: int = Query(1, ge=1, description="Page number"),
|
page: int = Query(1, ge=1, description="Page number"),
|
||||||
|
|
@ -210,7 +210,7 @@ async def get_workflows(
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Validate access
|
# Validate access
|
||||||
await _validateInstanceAccess(instanceId, context)
|
_validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
# Get service with feature instance context
|
# Get service with feature instance context
|
||||||
chatInterface = _getServiceChat(context, featureInstanceId=instanceId)
|
chatInterface = _getServiceChat(context, featureInstanceId=instanceId)
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.get("/config", response_model=DataNeutraliserConfig)
|
@router.get("/config", response_model=DataNeutraliserConfig)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_neutralization_config(
|
def get_neutralization_config(
|
||||||
request: Request,
|
request: Request,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> DataNeutraliserConfig:
|
) -> DataNeutraliserConfig:
|
||||||
|
|
@ -62,7 +62,7 @@ async def get_neutralization_config(
|
||||||
|
|
||||||
@router.post("/config", response_model=DataNeutraliserConfig)
|
@router.post("/config", response_model=DataNeutraliserConfig)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def save_neutralization_config(
|
def save_neutralization_config(
|
||||||
request: Request,
|
request: Request,
|
||||||
config_data: Dict[str, Any] = Body(...),
|
config_data: Dict[str, Any] = Body(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -83,7 +83,7 @@ async def save_neutralization_config(
|
||||||
|
|
||||||
@router.post("/neutralize-text", response_model=Dict[str, Any])
|
@router.post("/neutralize-text", response_model=Dict[str, Any])
|
||||||
@limiter.limit("20/minute")
|
@limiter.limit("20/minute")
|
||||||
async def neutralize_text(
|
def neutralize_text(
|
||||||
request: Request,
|
request: Request,
|
||||||
text_data: Dict[str, Any] = Body(...),
|
text_data: Dict[str, Any] = Body(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -115,7 +115,7 @@ async def neutralize_text(
|
||||||
|
|
||||||
@router.post("/resolve-text", response_model=Dict[str, str])
|
@router.post("/resolve-text", response_model=Dict[str, str])
|
||||||
@limiter.limit("20/minute")
|
@limiter.limit("20/minute")
|
||||||
async def resolve_text(
|
def resolve_text(
|
||||||
request: Request,
|
request: Request,
|
||||||
text_data: Dict[str, str] = Body(...),
|
text_data: Dict[str, str] = Body(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -146,7 +146,7 @@ async def resolve_text(
|
||||||
|
|
||||||
@router.get("/attributes", response_model=List[DataNeutralizerAttributes])
|
@router.get("/attributes", response_model=List[DataNeutralizerAttributes])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_neutralization_attributes(
|
def get_neutralization_attributes(
|
||||||
request: Request,
|
request: Request,
|
||||||
fileId: Optional[str] = Query(None, description="Filter by file ID"),
|
fileId: Optional[str] = Query(None, description="Filter by file ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -199,7 +199,7 @@ async def process_sharepoint_files(
|
||||||
|
|
||||||
@router.post("/batch-process", response_model=Dict[str, Any])
|
@router.post("/batch-process", response_model=Dict[str, Any])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def batch_process_files(
|
def batch_process_files(
|
||||||
request: Request,
|
request: Request,
|
||||||
files_data: List[Dict[str, Any]] = Body(...),
|
files_data: List[Dict[str, Any]] = Body(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -228,7 +228,7 @@ async def batch_process_files(
|
||||||
|
|
||||||
@router.get("/stats", response_model=Dict[str, Any])
|
@router.get("/stats", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_neutralization_stats(
|
def get_neutralization_stats(
|
||||||
request: Request,
|
request: Request,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
|
@ -248,7 +248,7 @@ async def get_neutralization_stats(
|
||||||
|
|
||||||
@router.delete("/attributes/{fileId}", response_model=Dict[str, str])
|
@router.delete("/attributes/{fileId}", response_model=Dict[str, str])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def cleanup_file_attributes(
|
def cleanup_file_attributes(
|
||||||
request: Request,
|
request: Request,
|
||||||
fileId: str = Path(..., description="File ID to cleanup attributes for"),
|
fileId: str = Path(..., description="File ID to cleanup attributes for"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ def _parsePagination(pagination: Optional[str]) -> Optional[PaginationParams]:
|
||||||
return None
|
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.
|
Validate that the user has access to the feature instance.
|
||||||
Returns the mandateId for the instance.
|
Returns the mandateId for the instance.
|
||||||
|
|
@ -132,14 +132,14 @@ _REALESTATE_ENTITY_MODELS = {
|
||||||
|
|
||||||
@router.get("/{instanceId}/attributes/{entityType}", response_model=Dict[str, Any])
|
@router.get("/{instanceId}/attributes/{entityType}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_entity_attributes(
|
def get_entity_attributes(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
entityType: str = Path(..., description="Entity type (e.g., Projekt, Parzelle)"),
|
entityType: str = Path(..., description="Entity type (e.g., Projekt, Parzelle)"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Get attribute definitions for a Real Estate entity. Used by FormGeneratorTable."""
|
"""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:
|
if entityType not in _REALESTATE_ENTITY_MODELS:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=404,
|
status_code=404,
|
||||||
|
|
@ -163,13 +163,13 @@ async def get_entity_attributes(
|
||||||
|
|
||||||
@router.get("/{instanceId}/projects/options", response_model=List[Dict[str, Any]])
|
@router.get("/{instanceId}/projects/options", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_project_options(
|
def get_project_options(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""Get project options for select dropdowns. Returns: [{ value, label }]"""
|
"""Get project options for select dropdowns. Returns: [{ value, label }]"""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
interface = getRealEstateInterface(
|
interface = getRealEstateInterface(
|
||||||
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
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]])
|
@router.get("/{instanceId}/parcels/options", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_parcel_options(
|
def get_parcel_options(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""Get parcel options for select dropdowns. Returns: [{ value, label }]"""
|
"""Get parcel options for select dropdowns. Returns: [{ value, label }]"""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
interface = getRealEstateInterface(
|
interface = getRealEstateInterface(
|
||||||
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
||||||
)
|
)
|
||||||
|
|
@ -197,14 +197,14 @@ async def get_parcel_options(
|
||||||
|
|
||||||
@router.get("/{instanceId}/projects", response_model=PaginatedResponse[Projekt])
|
@router.get("/{instanceId}/projects", response_model=PaginatedResponse[Projekt])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_projects(
|
def get_projects(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> PaginatedResponse[Projekt]:
|
) -> PaginatedResponse[Projekt]:
|
||||||
"""Get all projects for a feature instance with optional pagination."""
|
"""Get all projects for a feature instance with optional pagination."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
interface = getRealEstateInterface(
|
interface = getRealEstateInterface(
|
||||||
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
||||||
)
|
)
|
||||||
|
|
@ -241,14 +241,14 @@ async def get_projects(
|
||||||
|
|
||||||
@router.get("/{instanceId}/projects/{projectId}", response_model=Projekt)
|
@router.get("/{instanceId}/projects/{projectId}", response_model=Projekt)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_project_by_id(
|
def get_project_by_id(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
projectId: str = Path(..., description="Project ID"),
|
projectId: str = Path(..., description="Project ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Projekt:
|
) -> Projekt:
|
||||||
"""Get a single project by ID."""
|
"""Get a single project by ID."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
interface = getRealEstateInterface(
|
interface = getRealEstateInterface(
|
||||||
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
||||||
)
|
)
|
||||||
|
|
@ -260,14 +260,14 @@ async def get_project_by_id(
|
||||||
|
|
||||||
@router.post("/{instanceId}/projects", response_model=Projekt)
|
@router.post("/{instanceId}/projects", response_model=Projekt)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def create_project(
|
def create_project(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
data: Dict[str, Any] = Body(...),
|
data: Dict[str, Any] = Body(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Projekt:
|
) -> Projekt:
|
||||||
"""Create a new project."""
|
"""Create a new project."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
interface = getRealEstateInterface(
|
interface = getRealEstateInterface(
|
||||||
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
||||||
)
|
)
|
||||||
|
|
@ -284,7 +284,7 @@ async def create_project(
|
||||||
|
|
||||||
@router.put("/{instanceId}/projects/{projectId}", response_model=Projekt)
|
@router.put("/{instanceId}/projects/{projectId}", response_model=Projekt)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def update_project(
|
def update_project(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
projectId: str = Path(..., description="Project ID"),
|
projectId: str = Path(..., description="Project ID"),
|
||||||
|
|
@ -292,7 +292,7 @@ async def update_project(
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Projekt:
|
) -> Projekt:
|
||||||
"""Update a project."""
|
"""Update a project."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
interface = getRealEstateInterface(
|
interface = getRealEstateInterface(
|
||||||
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
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)
|
@router.delete("/{instanceId}/projects/{projectId}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def delete_project(
|
def delete_project(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
projectId: str = Path(..., description="Project ID"),
|
projectId: str = Path(..., description="Project ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Delete a project."""
|
"""Delete a project."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
interface = getRealEstateInterface(
|
interface = getRealEstateInterface(
|
||||||
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
||||||
)
|
)
|
||||||
|
|
@ -329,14 +329,14 @@ async def delete_project(
|
||||||
|
|
||||||
@router.get("/{instanceId}/parcels", response_model=PaginatedResponse[Parzelle])
|
@router.get("/{instanceId}/parcels", response_model=PaginatedResponse[Parzelle])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_parcels(
|
def get_parcels(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> PaginatedResponse[Parzelle]:
|
) -> PaginatedResponse[Parzelle]:
|
||||||
"""Get all parcels for a feature instance with optional pagination."""
|
"""Get all parcels for a feature instance with optional pagination."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
interface = getRealEstateInterface(
|
interface = getRealEstateInterface(
|
||||||
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
||||||
)
|
)
|
||||||
|
|
@ -373,14 +373,14 @@ async def get_parcels(
|
||||||
|
|
||||||
@router.get("/{instanceId}/parcels/{parcelId}", response_model=Parzelle)
|
@router.get("/{instanceId}/parcels/{parcelId}", response_model=Parzelle)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_parcel_by_id(
|
def get_parcel_by_id(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
parcelId: str = Path(..., description="Parcel ID"),
|
parcelId: str = Path(..., description="Parcel ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Parzelle:
|
) -> Parzelle:
|
||||||
"""Get a single parcel by ID."""
|
"""Get a single parcel by ID."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
interface = getRealEstateInterface(
|
interface = getRealEstateInterface(
|
||||||
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
||||||
)
|
)
|
||||||
|
|
@ -392,14 +392,14 @@ async def get_parcel_by_id(
|
||||||
|
|
||||||
@router.post("/{instanceId}/parcels", response_model=Parzelle)
|
@router.post("/{instanceId}/parcels", response_model=Parzelle)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def create_parcel(
|
def create_parcel(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
data: Dict[str, Any] = Body(...),
|
data: Dict[str, Any] = Body(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Parzelle:
|
) -> Parzelle:
|
||||||
"""Create a new parcel."""
|
"""Create a new parcel."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
interface = getRealEstateInterface(
|
interface = getRealEstateInterface(
|
||||||
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
||||||
)
|
)
|
||||||
|
|
@ -416,7 +416,7 @@ async def create_parcel(
|
||||||
|
|
||||||
@router.put("/{instanceId}/parcels/{parcelId}", response_model=Parzelle)
|
@router.put("/{instanceId}/parcels/{parcelId}", response_model=Parzelle)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def update_parcel(
|
def update_parcel(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
parcelId: str = Path(..., description="Parcel ID"),
|
parcelId: str = Path(..., description="Parcel ID"),
|
||||||
|
|
@ -424,7 +424,7 @@ async def update_parcel(
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Parzelle:
|
) -> Parzelle:
|
||||||
"""Update a parcel."""
|
"""Update a parcel."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
interface = getRealEstateInterface(
|
interface = getRealEstateInterface(
|
||||||
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
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)
|
@router.delete("/{instanceId}/parcels/{parcelId}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def delete_parcel(
|
def delete_parcel(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
parcelId: str = Path(..., description="Parcel ID"),
|
parcelId: str = Path(..., description="Parcel ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Delete a parcel."""
|
"""Delete a parcel."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
interface = getRealEstateInterface(
|
interface = getRealEstateInterface(
|
||||||
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
context.user, mandateId=mandateId, featureInstanceId=instanceId
|
||||||
)
|
)
|
||||||
|
|
@ -549,7 +549,7 @@ async def process_command(
|
||||||
|
|
||||||
@router.get("/tables", response_model=Dict[str, Any])
|
@router.get("/tables", response_model=Dict[str, Any])
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def get_available_tables(
|
def get_available_tables(
|
||||||
request: Request,
|
request: Request,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
|
@ -645,7 +645,7 @@ async def get_available_tables(
|
||||||
|
|
||||||
@router.get("/table/{table}", response_model=PaginatedResponse[Any])
|
@router.get("/table/{table}", response_model=PaginatedResponse[Any])
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def get_table_data(
|
def get_table_data(
|
||||||
request: Request,
|
request: Request,
|
||||||
table: str = Path(..., description="Table name (Projekt, Parzelle, Dokument, Gemeinde, Kanton, Land)"),
|
table: str = Path(..., description="Table name (Projekt, Parzelle, Dokument, Gemeinde, Kanton, Land)"),
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ def _parsePagination(pagination: Optional[str]) -> Optional[PaginationParams]:
|
||||||
return None
|
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.
|
Validate that the user has access to the feature instance.
|
||||||
Returns the mandateId for the instance.
|
Returns the mandateId for the instance.
|
||||||
|
|
@ -134,7 +134,7 @@ _TRUSTEE_ENTITY_MODELS = {
|
||||||
|
|
||||||
@router.get("/{instanceId}/attributes/{entityType}")
|
@router.get("/{instanceId}/attributes/{entityType}")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_entity_attributes(
|
def get_entity_attributes(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
entityType: str = Path(..., description="Entity type (e.g., TrusteeDocument)"),
|
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.
|
Used by FormGeneratorTable for dynamic column generation.
|
||||||
"""
|
"""
|
||||||
# Validate instance access
|
# Validate instance access
|
||||||
await _validateInstanceAccess(instanceId, context)
|
_validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
# Check if entity type is valid
|
# Check if entity type is valid
|
||||||
if entityType not in _TRUSTEE_ENTITY_MODELS:
|
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]])
|
@router.get("/mime-types/options", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_mime_type_options(
|
def get_mime_type_options(
|
||||||
request: Request,
|
request: Request,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> 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]])
|
@router.get("/{instanceId}/organisations/options", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_organisation_options(
|
def get_organisation_options(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""Get organisation options for select dropdowns. Returns: [{ value, label }]"""
|
"""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)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
result = interface.getAllOrganisations(None)
|
result = interface.getAllOrganisations(None)
|
||||||
items = result.items if hasattr(result, 'items') else result
|
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]])
|
@router.get("/{instanceId}/roles/options", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_role_options(
|
def get_role_options(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""Get role options for select dropdowns. Returns: [{ value, label }]"""
|
"""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)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
result = interface.getAllRoles(None)
|
result = interface.getAllRoles(None)
|
||||||
items = result.items if hasattr(result, 'items') else result
|
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]])
|
@router.get("/{instanceId}/contracts/options", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_contract_options(
|
def get_contract_options(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
organisationId: Optional[str] = Query(None, description="Optional: Filter by organisation ID"),
|
organisationId: Optional[str] = Query(None, description="Optional: Filter by organisation ID"),
|
||||||
|
|
@ -261,7 +261,7 @@ async def get_contract_options(
|
||||||
|
|
||||||
Returns: [{ value, label }]
|
Returns: [{ value, label }]
|
||||||
"""
|
"""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
|
|
||||||
if organisationId:
|
if organisationId:
|
||||||
|
|
@ -277,13 +277,13 @@ async def get_contract_options(
|
||||||
|
|
||||||
@router.get("/{instanceId}/documents/options", response_model=List[Dict[str, Any]])
|
@router.get("/{instanceId}/documents/options", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_document_options(
|
def get_document_options(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""Get document options for select dropdowns. Returns: [{ id, value, label }]"""
|
"""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)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
result = interface.getAllDocuments(None)
|
result = interface.getAllDocuments(None)
|
||||||
items = result.items if hasattr(result, 'items') else result
|
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]])
|
@router.get("/{instanceId}/positions/options", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_position_options(
|
def get_position_options(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""Get position options for select dropdowns. Returns: [{ id, value, label }]"""
|
"""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)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
result = interface.getAllPositions(None)
|
result = interface.getAllPositions(None)
|
||||||
items = result.items if hasattr(result, 'items') else result
|
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])
|
@router.get("/{instanceId}/organisations", response_model=PaginatedResponse[TrusteeOrganisation])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_organisations(
|
def get_organisations(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> PaginatedResponse[TrusteeOrganisation]:
|
) -> PaginatedResponse[TrusteeOrganisation]:
|
||||||
"""Get all organisations for a feature instance with optional pagination."""
|
"""Get all organisations for a feature instance with optional pagination."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
paginationParams = _parsePagination(pagination)
|
paginationParams = _parsePagination(pagination)
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
|
|
@ -356,14 +356,14 @@ async def get_organisations(
|
||||||
|
|
||||||
@router.get("/{instanceId}/organisations/{orgId}", response_model=TrusteeOrganisation)
|
@router.get("/{instanceId}/organisations/{orgId}", response_model=TrusteeOrganisation)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_organisation(
|
def get_organisation(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
orgId: str = Path(..., description="Organisation ID"),
|
orgId: str = Path(..., description="Organisation ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeOrganisation:
|
) -> TrusteeOrganisation:
|
||||||
"""Get a single organisation by ID."""
|
"""Get a single organisation by ID."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
org = interface.getOrganisation(orgId)
|
org = interface.getOrganisation(orgId)
|
||||||
|
|
@ -374,14 +374,14 @@ async def get_organisation(
|
||||||
|
|
||||||
@router.post("/{instanceId}/organisations", response_model=TrusteeOrganisation, status_code=201)
|
@router.post("/{instanceId}/organisations", response_model=TrusteeOrganisation, status_code=201)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_organisation(
|
def create_organisation(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
data: TrusteeOrganisation = Body(...),
|
data: TrusteeOrganisation = Body(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeOrganisation:
|
) -> TrusteeOrganisation:
|
||||||
"""Create a new organisation."""
|
"""Create a new organisation."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
result = interface.createOrganisation(data.model_dump())
|
result = interface.createOrganisation(data.model_dump())
|
||||||
|
|
@ -392,7 +392,7 @@ async def create_organisation(
|
||||||
|
|
||||||
@router.put("/{instanceId}/organisations/{orgId}", response_model=TrusteeOrganisation)
|
@router.put("/{instanceId}/organisations/{orgId}", response_model=TrusteeOrganisation)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_organisation(
|
def update_organisation(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
orgId: str = Path(..., description="Organisation ID"),
|
orgId: str = Path(..., description="Organisation ID"),
|
||||||
|
|
@ -400,7 +400,7 @@ async def update_organisation(
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeOrganisation:
|
) -> TrusteeOrganisation:
|
||||||
"""Update an organisation."""
|
"""Update an organisation."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
existing = interface.getOrganisation(orgId)
|
existing = interface.getOrganisation(orgId)
|
||||||
|
|
@ -415,14 +415,14 @@ async def update_organisation(
|
||||||
|
|
||||||
@router.delete("/{instanceId}/organisations/{orgId}")
|
@router.delete("/{instanceId}/organisations/{orgId}")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_organisation(
|
def delete_organisation(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
orgId: str = Path(..., description="Organisation ID"),
|
orgId: str = Path(..., description="Organisation ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Delete an organisation."""
|
"""Delete an organisation."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
existing = interface.getOrganisation(orgId)
|
existing = interface.getOrganisation(orgId)
|
||||||
|
|
@ -439,14 +439,14 @@ async def delete_organisation(
|
||||||
|
|
||||||
@router.get("/{instanceId}/roles", response_model=PaginatedResponse[TrusteeRole])
|
@router.get("/{instanceId}/roles", response_model=PaginatedResponse[TrusteeRole])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_roles(
|
def get_roles(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
pagination: Optional[str] = Query(None),
|
pagination: Optional[str] = Query(None),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> PaginatedResponse[TrusteeRole]:
|
) -> PaginatedResponse[TrusteeRole]:
|
||||||
"""Get all roles with optional pagination."""
|
"""Get all roles with optional pagination."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
paginationParams = _parsePagination(pagination)
|
paginationParams = _parsePagination(pagination)
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
|
|
@ -469,14 +469,14 @@ async def get_roles(
|
||||||
|
|
||||||
@router.get("/{instanceId}/roles/{roleId}", response_model=TrusteeRole)
|
@router.get("/{instanceId}/roles/{roleId}", response_model=TrusteeRole)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_role(
|
def get_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
roleId: str = Path(..., description="Role ID"),
|
roleId: str = Path(..., description="Role ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeRole:
|
) -> TrusteeRole:
|
||||||
"""Get a single role by ID."""
|
"""Get a single role by ID."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
role = interface.getRole(roleId)
|
role = interface.getRole(roleId)
|
||||||
|
|
@ -487,14 +487,14 @@ async def get_role(
|
||||||
|
|
||||||
@router.post("/{instanceId}/roles", response_model=TrusteeRole, status_code=201)
|
@router.post("/{instanceId}/roles", response_model=TrusteeRole, status_code=201)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_role(
|
def create_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
data: TrusteeRole = Body(...),
|
data: TrusteeRole = Body(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeRole:
|
) -> TrusteeRole:
|
||||||
"""Create a new role (sysadmin only)."""
|
"""Create a new role (sysadmin only)."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
result = interface.createRole(data.model_dump())
|
result = interface.createRole(data.model_dump())
|
||||||
|
|
@ -505,7 +505,7 @@ async def create_role(
|
||||||
|
|
||||||
@router.put("/{instanceId}/roles/{roleId}", response_model=TrusteeRole)
|
@router.put("/{instanceId}/roles/{roleId}", response_model=TrusteeRole)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_role(
|
def update_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
roleId: str = Path(...),
|
roleId: str = Path(...),
|
||||||
|
|
@ -513,7 +513,7 @@ async def update_role(
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeRole:
|
) -> TrusteeRole:
|
||||||
"""Update a role (sysadmin only)."""
|
"""Update a role (sysadmin only)."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
existing = interface.getRole(roleId)
|
existing = interface.getRole(roleId)
|
||||||
|
|
@ -528,14 +528,14 @@ async def update_role(
|
||||||
|
|
||||||
@router.delete("/{instanceId}/roles/{roleId}")
|
@router.delete("/{instanceId}/roles/{roleId}")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_role(
|
def delete_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
roleId: str = Path(...),
|
roleId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Delete a role (sysadmin only, fails if in use)."""
|
"""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)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
existing = interface.getRole(roleId)
|
existing = interface.getRole(roleId)
|
||||||
|
|
@ -552,14 +552,14 @@ async def delete_role(
|
||||||
|
|
||||||
@router.get("/{instanceId}/access", response_model=PaginatedResponse[TrusteeAccess])
|
@router.get("/{instanceId}/access", response_model=PaginatedResponse[TrusteeAccess])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_all_access(
|
def get_all_access(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
pagination: Optional[str] = Query(None),
|
pagination: Optional[str] = Query(None),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> PaginatedResponse[TrusteeAccess]:
|
) -> PaginatedResponse[TrusteeAccess]:
|
||||||
"""Get all access records with optional pagination."""
|
"""Get all access records with optional pagination."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
paginationParams = _parsePagination(pagination)
|
paginationParams = _parsePagination(pagination)
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
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)
|
@router.get("/{instanceId}/access/{accessId}", response_model=TrusteeAccess)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_access(
|
def get_access(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
accessId: str = Path(...),
|
accessId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeAccess:
|
) -> TrusteeAccess:
|
||||||
"""Get a single access record by ID."""
|
"""Get a single access record by ID."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
access = interface.getAccess(accessId)
|
access = interface.getAccess(accessId)
|
||||||
|
|
@ -600,14 +600,14 @@ async def get_access(
|
||||||
|
|
||||||
@router.get("/{instanceId}/access/organisation/{orgId}", response_model=List[TrusteeAccess])
|
@router.get("/{instanceId}/access/organisation/{orgId}", response_model=List[TrusteeAccess])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_access_by_organisation(
|
def get_access_by_organisation(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
orgId: str = Path(...),
|
orgId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[TrusteeAccess]:
|
) -> List[TrusteeAccess]:
|
||||||
"""Get all access records for an organisation."""
|
"""Get all access records for an organisation."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
return interface.getAccessByOrganisation(orgId)
|
return interface.getAccessByOrganisation(orgId)
|
||||||
|
|
@ -615,14 +615,14 @@ async def get_access_by_organisation(
|
||||||
|
|
||||||
@router.get("/{instanceId}/access/user/{userId}", response_model=List[TrusteeAccess])
|
@router.get("/{instanceId}/access/user/{userId}", response_model=List[TrusteeAccess])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_access_by_user(
|
def get_access_by_user(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
userId: str = Path(...),
|
userId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[TrusteeAccess]:
|
) -> List[TrusteeAccess]:
|
||||||
"""Get all access records for a user."""
|
"""Get all access records for a user."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
return interface.getAccessByUser(userId)
|
return interface.getAccessByUser(userId)
|
||||||
|
|
@ -630,14 +630,14 @@ async def get_access_by_user(
|
||||||
|
|
||||||
@router.post("/{instanceId}/access", response_model=TrusteeAccess, status_code=201)
|
@router.post("/{instanceId}/access", response_model=TrusteeAccess, status_code=201)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_access(
|
def create_access(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
data: TrusteeAccess = Body(...),
|
data: TrusteeAccess = Body(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeAccess:
|
) -> TrusteeAccess:
|
||||||
"""Create a new access record."""
|
"""Create a new access record."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
result = interface.createAccess(data.model_dump())
|
result = interface.createAccess(data.model_dump())
|
||||||
|
|
@ -648,7 +648,7 @@ async def create_access(
|
||||||
|
|
||||||
@router.put("/{instanceId}/access/{accessId}", response_model=TrusteeAccess)
|
@router.put("/{instanceId}/access/{accessId}", response_model=TrusteeAccess)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_access(
|
def update_access(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
accessId: str = Path(...),
|
accessId: str = Path(...),
|
||||||
|
|
@ -656,7 +656,7 @@ async def update_access(
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeAccess:
|
) -> TrusteeAccess:
|
||||||
"""Update an access record."""
|
"""Update an access record."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
existing = interface.getAccess(accessId)
|
existing = interface.getAccess(accessId)
|
||||||
|
|
@ -671,14 +671,14 @@ async def update_access(
|
||||||
|
|
||||||
@router.delete("/{instanceId}/access/{accessId}")
|
@router.delete("/{instanceId}/access/{accessId}")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_access(
|
def delete_access(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
accessId: str = Path(...),
|
accessId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Delete an access record."""
|
"""Delete an access record."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
existing = interface.getAccess(accessId)
|
existing = interface.getAccess(accessId)
|
||||||
|
|
@ -695,14 +695,14 @@ async def delete_access(
|
||||||
|
|
||||||
@router.get("/{instanceId}/contracts", response_model=PaginatedResponse[TrusteeContract])
|
@router.get("/{instanceId}/contracts", response_model=PaginatedResponse[TrusteeContract])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_contracts(
|
def get_contracts(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
pagination: Optional[str] = Query(None),
|
pagination: Optional[str] = Query(None),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> PaginatedResponse[TrusteeContract]:
|
) -> PaginatedResponse[TrusteeContract]:
|
||||||
"""Get all contracts with optional pagination."""
|
"""Get all contracts with optional pagination."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
paginationParams = _parsePagination(pagination)
|
paginationParams = _parsePagination(pagination)
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
|
|
@ -725,14 +725,14 @@ async def get_contracts(
|
||||||
|
|
||||||
@router.get("/{instanceId}/contracts/{contractId}", response_model=TrusteeContract)
|
@router.get("/{instanceId}/contracts/{contractId}", response_model=TrusteeContract)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_contract(
|
def get_contract(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
contractId: str = Path(...),
|
contractId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeContract:
|
) -> TrusteeContract:
|
||||||
"""Get a single contract by ID."""
|
"""Get a single contract by ID."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
contract = interface.getContract(contractId)
|
contract = interface.getContract(contractId)
|
||||||
|
|
@ -743,14 +743,14 @@ async def get_contract(
|
||||||
|
|
||||||
@router.get("/{instanceId}/contracts/organisation/{orgId}", response_model=List[TrusteeContract])
|
@router.get("/{instanceId}/contracts/organisation/{orgId}", response_model=List[TrusteeContract])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_contracts_by_organisation(
|
def get_contracts_by_organisation(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
orgId: str = Path(...),
|
orgId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[TrusteeContract]:
|
) -> List[TrusteeContract]:
|
||||||
"""Get all contracts for an organisation."""
|
"""Get all contracts for an organisation."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
return interface.getContractsByOrganisation(orgId)
|
return interface.getContractsByOrganisation(orgId)
|
||||||
|
|
@ -758,14 +758,14 @@ async def get_contracts_by_organisation(
|
||||||
|
|
||||||
@router.post("/{instanceId}/contracts", response_model=TrusteeContract, status_code=201)
|
@router.post("/{instanceId}/contracts", response_model=TrusteeContract, status_code=201)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_contract(
|
def create_contract(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
data: TrusteeContract = Body(...),
|
data: TrusteeContract = Body(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeContract:
|
) -> TrusteeContract:
|
||||||
"""Create a new contract."""
|
"""Create a new contract."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
result = interface.createContract(data.model_dump())
|
result = interface.createContract(data.model_dump())
|
||||||
|
|
@ -776,7 +776,7 @@ async def create_contract(
|
||||||
|
|
||||||
@router.put("/{instanceId}/contracts/{contractId}", response_model=TrusteeContract)
|
@router.put("/{instanceId}/contracts/{contractId}", response_model=TrusteeContract)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_contract(
|
def update_contract(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
contractId: str = Path(...),
|
contractId: str = Path(...),
|
||||||
|
|
@ -784,7 +784,7 @@ async def update_contract(
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeContract:
|
) -> TrusteeContract:
|
||||||
"""Update a contract (organisationId is immutable)."""
|
"""Update a contract (organisationId is immutable)."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
existing = interface.getContract(contractId)
|
existing = interface.getContract(contractId)
|
||||||
|
|
@ -799,14 +799,14 @@ async def update_contract(
|
||||||
|
|
||||||
@router.delete("/{instanceId}/contracts/{contractId}")
|
@router.delete("/{instanceId}/contracts/{contractId}")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_contract(
|
def delete_contract(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
contractId: str = Path(...),
|
contractId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Delete a contract."""
|
"""Delete a contract."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
existing = interface.getContract(contractId)
|
existing = interface.getContract(contractId)
|
||||||
|
|
@ -823,14 +823,14 @@ async def delete_contract(
|
||||||
|
|
||||||
@router.get("/{instanceId}/documents", response_model=PaginatedResponse[TrusteeDocument])
|
@router.get("/{instanceId}/documents", response_model=PaginatedResponse[TrusteeDocument])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_documents(
|
def get_documents(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
pagination: Optional[str] = Query(None),
|
pagination: Optional[str] = Query(None),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> PaginatedResponse[TrusteeDocument]:
|
) -> PaginatedResponse[TrusteeDocument]:
|
||||||
"""Get all documents (metadata only) with optional pagination."""
|
"""Get all documents (metadata only) with optional pagination."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
paginationParams = _parsePagination(pagination)
|
paginationParams = _parsePagination(pagination)
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
|
|
@ -853,14 +853,14 @@ async def get_documents(
|
||||||
|
|
||||||
@router.get("/{instanceId}/documents/{documentId}", response_model=TrusteeDocument)
|
@router.get("/{instanceId}/documents/{documentId}", response_model=TrusteeDocument)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_document(
|
def get_document(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
documentId: str = Path(...),
|
documentId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeDocument:
|
) -> TrusteeDocument:
|
||||||
"""Get document metadata by ID."""
|
"""Get document metadata by ID."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
doc = interface.getDocument(documentId)
|
doc = interface.getDocument(documentId)
|
||||||
|
|
@ -871,14 +871,14 @@ async def get_document(
|
||||||
|
|
||||||
@router.get("/{instanceId}/documents/{documentId}/data")
|
@router.get("/{instanceId}/documents/{documentId}/data")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def get_document_data(
|
def get_document_data(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
documentId: str = Path(...),
|
documentId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
):
|
):
|
||||||
"""Download document binary data."""
|
"""Download document binary data."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
doc = interface.getDocument(documentId)
|
doc = interface.getDocument(documentId)
|
||||||
|
|
@ -898,14 +898,14 @@ async def get_document_data(
|
||||||
|
|
||||||
@router.get("/{instanceId}/documents/contract/{contractId}", response_model=List[TrusteeDocument])
|
@router.get("/{instanceId}/documents/contract/{contractId}", response_model=List[TrusteeDocument])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_documents_by_contract(
|
def get_documents_by_contract(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
contractId: str = Path(...),
|
contractId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[TrusteeDocument]:
|
) -> List[TrusteeDocument]:
|
||||||
"""Get all documents for a contract."""
|
"""Get all documents for a contract."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
return interface.getDocumentsByContract(contractId)
|
return interface.getDocumentsByContract(contractId)
|
||||||
|
|
@ -919,7 +919,7 @@ async def create_document(
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeDocument:
|
) -> TrusteeDocument:
|
||||||
"""Create a new document. Accepts JSON body with optional base64-encoded documentData."""
|
"""Create a new document. Accepts JSON body with optional base64-encoded documentData."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
# Parse JSON body
|
# Parse JSON body
|
||||||
body = await request.json()
|
body = await request.json()
|
||||||
|
|
@ -959,7 +959,7 @@ async def upload_document(
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeDocument:
|
) -> TrusteeDocument:
|
||||||
"""Upload a document with multipart/form-data."""
|
"""Upload a document with multipart/form-data."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
# Read file content
|
# Read file content
|
||||||
fileContent = await file.read()
|
fileContent = await file.read()
|
||||||
|
|
@ -980,7 +980,7 @@ async def upload_document(
|
||||||
|
|
||||||
@router.put("/{instanceId}/documents/{documentId}", response_model=TrusteeDocument)
|
@router.put("/{instanceId}/documents/{documentId}", response_model=TrusteeDocument)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_document(
|
def update_document(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
documentId: str = Path(...),
|
documentId: str = Path(...),
|
||||||
|
|
@ -988,7 +988,7 @@ async def update_document(
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteeDocument:
|
) -> TrusteeDocument:
|
||||||
"""Update document metadata."""
|
"""Update document metadata."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
existing = interface.getDocument(documentId)
|
existing = interface.getDocument(documentId)
|
||||||
|
|
@ -1003,14 +1003,14 @@ async def update_document(
|
||||||
|
|
||||||
@router.delete("/{instanceId}/documents/{documentId}")
|
@router.delete("/{instanceId}/documents/{documentId}")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_document(
|
def delete_document(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
documentId: str = Path(...),
|
documentId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Delete a document."""
|
"""Delete a document."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
existing = interface.getDocument(documentId)
|
existing = interface.getDocument(documentId)
|
||||||
|
|
@ -1027,14 +1027,14 @@ async def delete_document(
|
||||||
|
|
||||||
@router.get("/{instanceId}/positions", response_model=PaginatedResponse[TrusteePosition])
|
@router.get("/{instanceId}/positions", response_model=PaginatedResponse[TrusteePosition])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_positions(
|
def get_positions(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
pagination: Optional[str] = Query(None),
|
pagination: Optional[str] = Query(None),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> PaginatedResponse[TrusteePosition]:
|
) -> PaginatedResponse[TrusteePosition]:
|
||||||
"""Get all positions with optional pagination."""
|
"""Get all positions with optional pagination."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
paginationParams = _parsePagination(pagination)
|
paginationParams = _parsePagination(pagination)
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
|
|
@ -1057,14 +1057,14 @@ async def get_positions(
|
||||||
|
|
||||||
@router.get("/{instanceId}/positions/{positionId}", response_model=TrusteePosition)
|
@router.get("/{instanceId}/positions/{positionId}", response_model=TrusteePosition)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_position(
|
def get_position(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
positionId: str = Path(...),
|
positionId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteePosition:
|
) -> TrusteePosition:
|
||||||
"""Get a single position by ID."""
|
"""Get a single position by ID."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
position = interface.getPosition(positionId)
|
position = interface.getPosition(positionId)
|
||||||
|
|
@ -1075,14 +1075,14 @@ async def get_position(
|
||||||
|
|
||||||
@router.get("/{instanceId}/positions/contract/{contractId}", response_model=List[TrusteePosition])
|
@router.get("/{instanceId}/positions/contract/{contractId}", response_model=List[TrusteePosition])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_positions_by_contract(
|
def get_positions_by_contract(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
contractId: str = Path(...),
|
contractId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[TrusteePosition]:
|
) -> List[TrusteePosition]:
|
||||||
"""Get all positions for a contract."""
|
"""Get all positions for a contract."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
return interface.getPositionsByContract(contractId)
|
return interface.getPositionsByContract(contractId)
|
||||||
|
|
@ -1090,14 +1090,14 @@ async def get_positions_by_contract(
|
||||||
|
|
||||||
@router.get("/{instanceId}/positions/organisation/{orgId}", response_model=List[TrusteePosition])
|
@router.get("/{instanceId}/positions/organisation/{orgId}", response_model=List[TrusteePosition])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_positions_by_organisation(
|
def get_positions_by_organisation(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
orgId: str = Path(...),
|
orgId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[TrusteePosition]:
|
) -> List[TrusteePosition]:
|
||||||
"""Get all positions for an organisation."""
|
"""Get all positions for an organisation."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
return interface.getPositionsByOrganisation(orgId)
|
return interface.getPositionsByOrganisation(orgId)
|
||||||
|
|
@ -1105,14 +1105,14 @@ async def get_positions_by_organisation(
|
||||||
|
|
||||||
@router.post("/{instanceId}/positions", response_model=TrusteePosition, status_code=201)
|
@router.post("/{instanceId}/positions", response_model=TrusteePosition, status_code=201)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_position(
|
def create_position(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
data: TrusteePosition = Body(...),
|
data: TrusteePosition = Body(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteePosition:
|
) -> TrusteePosition:
|
||||||
"""Create a new position."""
|
"""Create a new position."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
result = interface.createPosition(data.model_dump())
|
result = interface.createPosition(data.model_dump())
|
||||||
|
|
@ -1123,7 +1123,7 @@ async def create_position(
|
||||||
|
|
||||||
@router.put("/{instanceId}/positions/{positionId}", response_model=TrusteePosition)
|
@router.put("/{instanceId}/positions/{positionId}", response_model=TrusteePosition)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_position(
|
def update_position(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
positionId: str = Path(...),
|
positionId: str = Path(...),
|
||||||
|
|
@ -1131,7 +1131,7 @@ async def update_position(
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteePosition:
|
) -> TrusteePosition:
|
||||||
"""Update a position."""
|
"""Update a position."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
existing = interface.getPosition(positionId)
|
existing = interface.getPosition(positionId)
|
||||||
|
|
@ -1146,14 +1146,14 @@ async def update_position(
|
||||||
|
|
||||||
@router.delete("/{instanceId}/positions/{positionId}")
|
@router.delete("/{instanceId}/positions/{positionId}")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_position(
|
def delete_position(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
positionId: str = Path(...),
|
positionId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Delete a position."""
|
"""Delete a position."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
existing = interface.getPosition(positionId)
|
existing = interface.getPosition(positionId)
|
||||||
|
|
@ -1170,7 +1170,7 @@ async def delete_position(
|
||||||
|
|
||||||
@router.get("/{instanceId}/position-documents")
|
@router.get("/{instanceId}/position-documents")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_position_documents(
|
def get_position_documents(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
pagination: Optional[str] = Query(None),
|
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.
|
Each item includes _permissions: { canUpdate, canDelete } for row-level permission UI.
|
||||||
"""
|
"""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
paginationParams = _parsePagination(pagination)
|
paginationParams = _parsePagination(pagination)
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
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)
|
@router.get("/{instanceId}/position-documents/{linkId}", response_model=TrusteePositionDocument)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_position_document(
|
def get_position_document(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
linkId: str = Path(...),
|
linkId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteePositionDocument:
|
) -> TrusteePositionDocument:
|
||||||
"""Get a single position-document link by ID."""
|
"""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)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
link = interface.getPositionDocument(linkId)
|
link = interface.getPositionDocument(linkId)
|
||||||
|
|
@ -1221,14 +1221,14 @@ async def get_position_document(
|
||||||
|
|
||||||
@router.get("/{instanceId}/position-documents/position/{positionId}", response_model=List[TrusteePositionDocument])
|
@router.get("/{instanceId}/position-documents/position/{positionId}", response_model=List[TrusteePositionDocument])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_documents_for_position(
|
def get_documents_for_position(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
positionId: str = Path(...),
|
positionId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[TrusteePositionDocument]:
|
) -> List[TrusteePositionDocument]:
|
||||||
"""Get all document links for a position."""
|
"""Get all document links for a position."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
return interface.getDocumentsForPosition(positionId)
|
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])
|
@router.get("/{instanceId}/position-documents/document/{documentId}", response_model=List[TrusteePositionDocument])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_positions_for_document(
|
def get_positions_for_document(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
documentId: str = Path(...),
|
documentId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[TrusteePositionDocument]:
|
) -> List[TrusteePositionDocument]:
|
||||||
"""Get all position links for a document."""
|
"""Get all position links for a document."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
return interface.getPositionsForDocument(documentId)
|
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)
|
@router.post("/{instanceId}/position-documents", response_model=TrusteePositionDocument, status_code=201)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_position_document(
|
def create_position_document(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
data: TrusteePositionDocument = Body(...),
|
data: TrusteePositionDocument = Body(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteePositionDocument:
|
) -> TrusteePositionDocument:
|
||||||
"""Create a new position-document link."""
|
"""Create a new position-document link."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
result = interface.createPositionDocument(data.model_dump())
|
result = interface.createPositionDocument(data.model_dump())
|
||||||
|
|
@ -1269,7 +1269,7 @@ async def create_position_document(
|
||||||
|
|
||||||
@router.put("/{instanceId}/position-documents/{linkId}", response_model=TrusteePositionDocument)
|
@router.put("/{instanceId}/position-documents/{linkId}", response_model=TrusteePositionDocument)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_position_document(
|
def update_position_document(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
linkId: str = Path(...),
|
linkId: str = Path(...),
|
||||||
|
|
@ -1277,7 +1277,7 @@ async def update_position_document(
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> TrusteePositionDocument:
|
) -> TrusteePositionDocument:
|
||||||
"""Update a position-document link."""
|
"""Update a position-document link."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
result = interface.updatePositionDocument(linkId, data.model_dump(exclude_unset=True))
|
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}")
|
@router.delete("/{instanceId}/position-documents/{linkId}")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_position_document(
|
def delete_position_document(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
linkId: str = Path(...),
|
linkId: str = Path(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Delete a position-document link."""
|
"""Delete a position-document link."""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
|
||||||
existing = interface.getPositionDocument(linkId)
|
existing = interface.getPositionDocument(linkId)
|
||||||
|
|
@ -1314,14 +1314,14 @@ async def delete_position_document(
|
||||||
from modules.datamodels.datamodelRbac import Role, AccessRule, AccessRuleContext
|
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.
|
Validate that the user has admin access to the feature instance.
|
||||||
Returns the mandateId if authorized.
|
Returns the mandateId if authorized.
|
||||||
|
|
||||||
This checks for the RESOURCE permission 'instance-roles.manage'.
|
This checks for the RESOURCE permission 'instance-roles.manage'.
|
||||||
"""
|
"""
|
||||||
mandateId = await _validateInstanceAccess(instanceId, context)
|
mandateId = _validateInstanceAccess(instanceId, context)
|
||||||
|
|
||||||
# SysAdmin always has access
|
# SysAdmin always has access
|
||||||
if context.user.isSysAdmin:
|
if context.user.isSysAdmin:
|
||||||
|
|
@ -1350,7 +1350,7 @@ async def _validateInstanceAdmin(instanceId: str, context: RequestContext) -> st
|
||||||
|
|
||||||
@router.get("/{instanceId}/instance-roles", response_model=PaginatedResponse)
|
@router.get("/{instanceId}/instance-roles", response_model=PaginatedResponse)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_instance_roles(
|
def get_instance_roles(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -1359,7 +1359,7 @@ async def get_instance_roles(
|
||||||
Get all roles for this feature instance.
|
Get all roles for this feature instance.
|
||||||
Requires feature admin permission.
|
Requires feature admin permission.
|
||||||
"""
|
"""
|
||||||
mandateId = await _validateInstanceAdmin(instanceId, context)
|
mandateId = _validateInstanceAdmin(instanceId, context)
|
||||||
|
|
||||||
rootInterface = getRootInterface()
|
rootInterface = getRootInterface()
|
||||||
|
|
||||||
|
|
@ -1374,14 +1374,14 @@ async def get_instance_roles(
|
||||||
|
|
||||||
@router.get("/{instanceId}/instance-roles/{roleId}", response_model=Dict[str, Any])
|
@router.get("/{instanceId}/instance-roles/{roleId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_instance_role(
|
def get_instance_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
roleId: str = Path(..., description="Role ID"),
|
roleId: str = Path(..., description="Role ID"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Get a specific instance role."""
|
"""Get a specific instance role."""
|
||||||
mandateId = await _validateInstanceAdmin(instanceId, context)
|
mandateId = _validateInstanceAdmin(instanceId, context)
|
||||||
|
|
||||||
rootInterface = getRootInterface()
|
rootInterface = getRootInterface()
|
||||||
role = rootInterface.getRole(roleId)
|
role = rootInterface.getRole(roleId)
|
||||||
|
|
@ -1398,7 +1398,7 @@ async def get_instance_role(
|
||||||
|
|
||||||
@router.get("/{instanceId}/instance-roles/{roleId}/rules", response_model=PaginatedResponse)
|
@router.get("/{instanceId}/instance-roles/{roleId}/rules", response_model=PaginatedResponse)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_instance_role_rules(
|
def get_instance_role_rules(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
roleId: str = Path(..., description="Role 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.
|
Get all AccessRules for a specific instance role.
|
||||||
Requires feature admin permission.
|
Requires feature admin permission.
|
||||||
"""
|
"""
|
||||||
mandateId = await _validateInstanceAdmin(instanceId, context)
|
mandateId = _validateInstanceAdmin(instanceId, context)
|
||||||
|
|
||||||
rootInterface = getRootInterface()
|
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)
|
@router.post("/{instanceId}/instance-roles/{roleId}/rules", response_model=Dict[str, Any], status_code=201)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_instance_role_rule(
|
def create_instance_role_rule(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
roleId: str = Path(..., description="Role 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.
|
Create a new AccessRule for an instance role.
|
||||||
Requires feature admin permission.
|
Requires feature admin permission.
|
||||||
"""
|
"""
|
||||||
mandateId = await _validateInstanceAdmin(instanceId, context)
|
mandateId = _validateInstanceAdmin(instanceId, context)
|
||||||
|
|
||||||
rootInterface = getRootInterface()
|
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])
|
@router.put("/{instanceId}/instance-roles/{roleId}/rules/{ruleId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_instance_role_rule(
|
def update_instance_role_rule(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
roleId: str = Path(..., description="Role 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.
|
Only view, read, create, update, delete can be changed.
|
||||||
Requires feature admin permission.
|
Requires feature admin permission.
|
||||||
"""
|
"""
|
||||||
mandateId = await _validateInstanceAdmin(instanceId, context)
|
mandateId = _validateInstanceAdmin(instanceId, context)
|
||||||
|
|
||||||
rootInterface = getRootInterface()
|
rootInterface = getRootInterface()
|
||||||
|
|
||||||
|
|
@ -1530,7 +1530,7 @@ async def update_instance_role_rule(
|
||||||
|
|
||||||
@router.delete("/{instanceId}/instance-roles/{roleId}/rules/{ruleId}")
|
@router.delete("/{instanceId}/instance-roles/{roleId}/rules/{ruleId}")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_instance_role_rule(
|
def delete_instance_role_rule(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str = Path(..., description="Feature Instance ID"),
|
instanceId: str = Path(..., description="Feature Instance ID"),
|
||||||
roleId: str = Path(..., description="Role ID"),
|
roleId: str = Path(..., description="Role ID"),
|
||||||
|
|
@ -1541,7 +1541,7 @@ async def delete_instance_role_rule(
|
||||||
Delete an AccessRule for an instance role.
|
Delete an AccessRule for an instance role.
|
||||||
Requires feature admin permission.
|
Requires feature admin permission.
|
||||||
"""
|
"""
|
||||||
mandateId = await _validateInstanceAdmin(instanceId, context)
|
mandateId = _validateInstanceAdmin(instanceId, context)
|
||||||
|
|
||||||
rootInterface = getRootInterface()
|
rootInterface = getRootInterface()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ router.mount(
|
||||||
|
|
||||||
@router.get("/")
|
@router.get("/")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def root(request: Request) -> Dict[str, str]:
|
def root(request: Request) -> Dict[str, str]:
|
||||||
"""API status endpoint"""
|
"""API status endpoint"""
|
||||||
# Validate required configuration values
|
# Validate required configuration values
|
||||||
allowedOrigins = APP_CONFIG.get("APP_ALLOWED_ORIGINS")
|
allowedOrigins = APP_CONFIG.get("APP_ALLOWED_ORIGINS")
|
||||||
|
|
@ -51,7 +51,7 @@ async def root(request: Request) -> Dict[str, str]:
|
||||||
|
|
||||||
@router.get("/api/environment")
|
@router.get("/api/environment")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_environment(
|
def get_environment(
|
||||||
request: Request, currentUser: Dict[str, Any] = Depends(getCurrentUser)
|
request: Request, currentUser: Dict[str, Any] = Depends(getCurrentUser)
|
||||||
) -> Dict[str, str]:
|
) -> Dict[str, str]:
|
||||||
"""Get environment configuration for frontend"""
|
"""Get environment configuration for frontend"""
|
||||||
|
|
@ -82,13 +82,13 @@ async def get_environment(
|
||||||
|
|
||||||
@router.options("/{fullPath:path}")
|
@router.options("/{fullPath:path}")
|
||||||
@limiter.limit("60/minute")
|
@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)
|
return Response(status_code=200)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/favicon.ico")
|
@router.get("/favicon.ico")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def favicon(request: Request) -> FileResponse:
|
def favicon(request: Request) -> FileResponse:
|
||||||
favicon_path = staticFolder / "favicon.ico"
|
favicon_path = staticFolder / "favicon.ico"
|
||||||
if not favicon_path.exists():
|
if not favicon_path.exists():
|
||||||
raise HTTPException(status_code=404, detail="Favicon not found")
|
raise HTTPException(status_code=404, detail="Favicon not found")
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.get("")
|
@router.get("")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_all_automation_events(
|
def get_all_automation_events(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
|
|
@ -107,7 +107,7 @@ async def sync_all_automation_events(
|
||||||
|
|
||||||
@router.post("/{eventId}/remove")
|
@router.post("/{eventId}/remove")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def remove_event(
|
def remove_event(
|
||||||
request: Request,
|
request: Request,
|
||||||
eventId: str = Path(..., description="Event ID to remove"),
|
eventId: str = Path(..., description="Event ID to remove"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class SyncRolesResult(BaseModel):
|
||||||
|
|
||||||
@router.get("/", response_model=List[Dict[str, Any]])
|
@router.get("/", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def list_features(
|
def list_features(
|
||||||
request: Request,
|
request: Request,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
|
|
@ -105,7 +105,7 @@ class FeaturesMyResponse(BaseModel):
|
||||||
|
|
||||||
@router.get("/my", response_model=FeaturesMyResponse)
|
@router.get("/my", response_model=FeaturesMyResponse)
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_my_feature_instances(
|
def get_my_feature_instances(
|
||||||
request: Request,
|
request: Request,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> FeaturesMyResponse:
|
) -> FeaturesMyResponse:
|
||||||
|
|
@ -332,7 +332,7 @@ def _mergeAccessLevel(current: str, new: str) -> str:
|
||||||
|
|
||||||
@router.post("/", response_model=Dict[str, Any])
|
@router.post("/", response_model=Dict[str, Any])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_feature(
|
def create_feature(
|
||||||
request: Request,
|
request: Request,
|
||||||
code: str = Query(..., description="Unique feature code"),
|
code: str = Query(..., description="Unique feature code"),
|
||||||
label: Dict[str, str] = None,
|
label: Dict[str, str] = None,
|
||||||
|
|
@ -387,7 +387,7 @@ async def create_feature(
|
||||||
|
|
||||||
@router.get("/instances", response_model=List[Dict[str, Any]])
|
@router.get("/instances", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def list_feature_instances(
|
def list_feature_instances(
|
||||||
request: Request,
|
request: Request,
|
||||||
featureCode: Optional[str] = Query(None, description="Filter by feature code"),
|
featureCode: Optional[str] = Query(None, description="Filter by feature code"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -429,7 +429,7 @@ async def list_feature_instances(
|
||||||
|
|
||||||
@router.get("/instances/{instanceId}", response_model=Dict[str, Any])
|
@router.get("/instances/{instanceId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_feature_instance(
|
def get_feature_instance(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str,
|
instanceId: str,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -473,7 +473,7 @@ async def get_feature_instance(
|
||||||
|
|
||||||
@router.post("/instances", response_model=Dict[str, Any])
|
@router.post("/instances", response_model=Dict[str, Any])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_feature_instance(
|
def create_feature_instance(
|
||||||
request: Request,
|
request: Request,
|
||||||
data: FeatureInstanceCreate,
|
data: FeatureInstanceCreate,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -540,7 +540,7 @@ async def create_feature_instance(
|
||||||
|
|
||||||
@router.delete("/instances/{instanceId}", response_model=Dict[str, str])
|
@router.delete("/instances/{instanceId}", response_model=Dict[str, str])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_feature_instance(
|
def delete_feature_instance(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str,
|
instanceId: str,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -605,7 +605,7 @@ class FeatureInstanceUpdate(BaseModel):
|
||||||
|
|
||||||
@router.put("/instances/{instanceId}", response_model=Dict[str, Any])
|
@router.put("/instances/{instanceId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def updateFeatureInstance(
|
def updateFeatureInstance(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str,
|
instanceId: str,
|
||||||
data: FeatureInstanceUpdate,
|
data: FeatureInstanceUpdate,
|
||||||
|
|
@ -682,7 +682,7 @@ async def updateFeatureInstance(
|
||||||
|
|
||||||
@router.post("/instances/{instanceId}/sync-roles", response_model=SyncRolesResult)
|
@router.post("/instances/{instanceId}/sync-roles", response_model=SyncRolesResult)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def sync_instance_roles(
|
def sync_instance_roles(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str,
|
instanceId: str,
|
||||||
addOnly: bool = Query(True, description="Only add missing roles, don't remove extras"),
|
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]])
|
@router.get("/templates/roles", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def list_template_roles(
|
def list_template_roles(
|
||||||
request: Request,
|
request: Request,
|
||||||
featureCode: Optional[str] = Query(None, description="Filter by feature code"),
|
featureCode: Optional[str] = Query(None, description="Filter by feature code"),
|
||||||
sysAdmin: User = Depends(requireSysAdmin)
|
sysAdmin: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -779,7 +779,7 @@ async def list_template_roles(
|
||||||
|
|
||||||
@router.post("/templates/roles", response_model=Dict[str, Any])
|
@router.post("/templates/roles", response_model=Dict[str, Any])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_template_role(
|
def create_template_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
roleLabel: str = Query(..., description="Role label (e.g., 'admin', 'viewer')"),
|
roleLabel: str = Query(..., description="Role label (e.g., 'admin', 'viewer')"),
|
||||||
featureCode: str = Query(..., description="Feature code this role belongs to"),
|
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])
|
@router.get("/instances/{instanceId}/users", response_model=List[FeatureInstanceUserResponse])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def list_feature_instance_users(
|
def list_feature_instance_users(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str,
|
instanceId: str,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -942,7 +942,7 @@ async def list_feature_instance_users(
|
||||||
|
|
||||||
@router.post("/instances/{instanceId}/users", response_model=Dict[str, Any])
|
@router.post("/instances/{instanceId}/users", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def add_user_to_feature_instance(
|
def add_user_to_feature_instance(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str,
|
instanceId: str,
|
||||||
data: FeatureInstanceUserCreate,
|
data: FeatureInstanceUserCreate,
|
||||||
|
|
@ -1043,7 +1043,7 @@ async def add_user_to_feature_instance(
|
||||||
|
|
||||||
@router.delete("/instances/{instanceId}/users/{userId}", response_model=Dict[str, str])
|
@router.delete("/instances/{instanceId}/users/{userId}", response_model=Dict[str, str])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def remove_user_from_feature_instance(
|
def remove_user_from_feature_instance(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str,
|
instanceId: str,
|
||||||
userId: 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])
|
@router.put("/instances/{instanceId}/users/{userId}/roles", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def update_feature_instance_user_roles(
|
def update_feature_instance_user_roles(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str,
|
instanceId: str,
|
||||||
userId: 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]])
|
@router.get("/instances/{instanceId}/available-roles", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_feature_instance_available_roles(
|
def get_feature_instance_available_roles(
|
||||||
request: Request,
|
request: Request,
|
||||||
instanceId: str,
|
instanceId: str,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -1280,7 +1280,7 @@ async def get_feature_instance_available_roles(
|
||||||
|
|
||||||
@router.get("/{featureCode}", response_model=Dict[str, Any])
|
@router.get("/{featureCode}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_feature(
|
def get_feature(
|
||||||
request: Request,
|
request: Request,
|
||||||
featureCode: str,
|
featureCode: str,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ class RbacImportResult(BaseModel):
|
||||||
|
|
||||||
@router.get("/export/global", response_model=RbacExportData)
|
@router.get("/export/global", response_model=RbacExportData)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def export_global_rbac(
|
def export_global_rbac(
|
||||||
request: Request,
|
request: Request,
|
||||||
sysAdmin: User = Depends(requireSysAdmin)
|
sysAdmin: User = Depends(requireSysAdmin)
|
||||||
) -> RbacExportData:
|
) -> RbacExportData:
|
||||||
|
|
@ -281,7 +281,7 @@ async def import_global_rbac(
|
||||||
|
|
||||||
@router.get("/export/mandate", response_model=RbacExportData)
|
@router.get("/export/mandate", response_model=RbacExportData)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def export_mandate_rbac(
|
def export_mandate_rbac(
|
||||||
request: Request,
|
request: Request,
|
||||||
includeFeatureInstances: bool = True,
|
includeFeatureInstances: bool = True,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.get("/", response_model=List[Dict[str, Any]])
|
@router.get("/", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def list_roles(
|
def list_roles(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
|
|
@ -113,7 +113,7 @@ async def list_roles(
|
||||||
|
|
||||||
@router.get("/options", response_model=List[Dict[str, Any]])
|
@router.get("/options", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_role_options(
|
def get_role_options(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
|
|
@ -154,7 +154,7 @@ async def get_role_options(
|
||||||
|
|
||||||
@router.post("/", response_model=Dict[str, Any])
|
@router.post("/", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def create_role(
|
def create_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
role: Role = Body(...),
|
role: Role = Body(...),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -198,7 +198,7 @@ async def create_role(
|
||||||
|
|
||||||
@router.get("/{roleId}", response_model=Dict[str, Any])
|
@router.get("/{roleId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_role(
|
def get_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
roleId: str = Path(..., description="Role ID"),
|
roleId: str = Path(..., description="Role ID"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -242,7 +242,7 @@ async def get_role(
|
||||||
|
|
||||||
@router.put("/{roleId}", response_model=Dict[str, Any])
|
@router.put("/{roleId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def update_role(
|
def update_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
roleId: str = Path(..., description="Role ID"),
|
roleId: str = Path(..., description="Role ID"),
|
||||||
role: Role = Body(...),
|
role: Role = Body(...),
|
||||||
|
|
@ -290,7 +290,7 @@ async def update_role(
|
||||||
|
|
||||||
@router.delete("/{roleId}", response_model=Dict[str, str])
|
@router.delete("/{roleId}", response_model=Dict[str, str])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def delete_role(
|
def delete_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
roleId: str = Path(..., description="Role ID"),
|
roleId: str = Path(..., description="Role ID"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -334,7 +334,7 @@ async def delete_role(
|
||||||
|
|
||||||
@router.get("/users", response_model=List[Dict[str, Any]])
|
@router.get("/users", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def list_users_with_roles(
|
def list_users_with_roles(
|
||||||
request: Request,
|
request: Request,
|
||||||
roleLabel: Optional[str] = Query(None, description="Filter by role label"),
|
roleLabel: Optional[str] = Query(None, description="Filter by role label"),
|
||||||
mandateId: Optional[str] = Query(None, description="Filter by mandate ID (via UserMandate)"),
|
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])
|
@router.get("/users/{userId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_user_roles(
|
def get_user_roles(
|
||||||
request: Request,
|
request: Request,
|
||||||
userId: str = Path(..., description="User ID"),
|
userId: str = Path(..., description="User ID"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -446,7 +446,7 @@ async def get_user_roles(
|
||||||
|
|
||||||
@router.put("/users/{userId}/roles", response_model=Dict[str, Any])
|
@router.put("/users/{userId}/roles", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def update_user_roles(
|
def update_user_roles(
|
||||||
request: Request,
|
request: Request,
|
||||||
userId: str = Path(..., description="User ID"),
|
userId: str = Path(..., description="User ID"),
|
||||||
newRoleLabels: List[str] = Body(..., description="List of role labels to assign"),
|
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])
|
@router.post("/users/{userId}/roles/{roleLabel}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def add_user_role(
|
def add_user_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
userId: str = Path(..., description="User ID"),
|
userId: str = Path(..., description="User ID"),
|
||||||
roleLabel: str = Path(..., description="Role label to add"),
|
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])
|
@router.delete("/users/{userId}/roles/{roleLabel}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def remove_user_role(
|
def remove_user_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
userId: str = Path(..., description="User ID"),
|
userId: str = Path(..., description="User ID"),
|
||||||
roleLabel: str = Path(..., description="Role label to remove"),
|
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]])
|
@router.get("/roles/{roleLabel}/users", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_users_with_role(
|
def get_users_with_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
roleLabel: str = Path(..., description="Role label"),
|
roleLabel: str = Path(..., description="Role label"),
|
||||||
mandateId: Optional[str] = Query(None, description="Filter by mandate ID (via UserMandate)"),
|
mandateId: Optional[str] = Query(None, description="Filter by mandate ID (via UserMandate)"),
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.get("/permissions", response_model=UserPermissions)
|
@router.get("/permissions", response_model=UserPermissions)
|
||||||
@limiter.limit("300/minute") # Raised from 60 - sidebar checks many pages individually
|
@limiter.limit("300/minute") # Raised from 60 - sidebar checks many pages individually
|
||||||
async def get_permissions(
|
def get_permissions(
|
||||||
request: Request,
|
request: Request,
|
||||||
context: str = Query(..., description="Context type: DATA, UI, or RESOURCE"),
|
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)"),
|
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])
|
@router.get("/permissions/all", response_model=Dict[str, Any])
|
||||||
@limiter.limit("120/minute") # Raised from 30 - optimized endpoint for bulk permission fetch
|
@limiter.limit("120/minute") # Raised from 30 - optimized endpoint for bulk permission fetch
|
||||||
async def get_all_permissions(
|
def get_all_permissions(
|
||||||
request: Request,
|
request: Request,
|
||||||
context: Optional[str] = Query(None, description="Context type: UI or RESOURCE (if not provided, returns both)"),
|
context: Optional[str] = Query(None, description="Context type: UI or RESOURCE (if not provided, returns both)"),
|
||||||
reqContext: RequestContext = Depends(getRequestContext)
|
reqContext: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -293,7 +293,7 @@ async def get_all_permissions(
|
||||||
|
|
||||||
@router.get("/rules", response_model=PaginatedResponse)
|
@router.get("/rules", response_model=PaginatedResponse)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_access_rules(
|
def get_access_rules(
|
||||||
request: Request,
|
request: Request,
|
||||||
roleLabel: Optional[str] = Query(None, description="Filter by role label"),
|
roleLabel: Optional[str] = Query(None, description="Filter by role label"),
|
||||||
context: Optional[str] = Query(None, description="Filter by context (DATA, UI, RESOURCE)"),
|
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)
|
@router.get("/rules/by-role/{roleId}", response_model=PaginatedResponse)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_access_rules_by_role(
|
def get_access_rules_by_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
roleId: str = Path(..., description="Role ID to get rules for"),
|
roleId: str = Path(..., description="Role ID to get rules for"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -420,7 +420,7 @@ async def get_access_rules_by_role(
|
||||||
|
|
||||||
@router.get("/rules/{ruleId}", response_model=dict)
|
@router.get("/rules/{ruleId}", response_model=dict)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_access_rule(
|
def get_access_rule(
|
||||||
request: Request,
|
request: Request,
|
||||||
ruleId: str = Path(..., description="Access rule ID"),
|
ruleId: str = Path(..., description="Access rule ID"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -462,7 +462,7 @@ async def get_access_rule(
|
||||||
|
|
||||||
@router.post("/rules", response_model=dict)
|
@router.post("/rules", response_model=dict)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def create_access_rule(
|
def create_access_rule(
|
||||||
request: Request,
|
request: Request,
|
||||||
accessRuleData: dict = Body(..., description="Access rule data"),
|
accessRuleData: dict = Body(..., description="Access rule data"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -528,7 +528,7 @@ async def create_access_rule(
|
||||||
|
|
||||||
@router.put("/rules/{ruleId}", response_model=dict)
|
@router.put("/rules/{ruleId}", response_model=dict)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def update_access_rule(
|
def update_access_rule(
|
||||||
request: Request,
|
request: Request,
|
||||||
ruleId: str = Path(..., description="Access rule ID"),
|
ruleId: str = Path(..., description="Access rule ID"),
|
||||||
accessRuleData: dict = Body(..., description="Updated access rule data"),
|
accessRuleData: dict = Body(..., description="Updated access rule data"),
|
||||||
|
|
@ -611,7 +611,7 @@ async def update_access_rule(
|
||||||
|
|
||||||
@router.delete("/rules/{ruleId}")
|
@router.delete("/rules/{ruleId}")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def delete_access_rule(
|
def delete_access_rule(
|
||||||
request: Request,
|
request: Request,
|
||||||
ruleId: str = Path(..., description="Access rule ID"),
|
ruleId: str = Path(..., description="Access rule ID"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -669,7 +669,7 @@ async def delete_access_rule(
|
||||||
|
|
||||||
@router.get("/roles", response_model=PaginatedResponse)
|
@router.get("/roles", response_model=PaginatedResponse)
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def list_roles(
|
def list_roles(
|
||||||
request: Request,
|
request: Request,
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
||||||
includeTemplates: bool = Query(False, description="Include feature template roles"),
|
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]])
|
@router.get("/roles/options", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_role_options(
|
def get_role_options(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
|
|
@ -879,7 +879,7 @@ async def get_role_options(
|
||||||
|
|
||||||
@router.post("/roles", response_model=Dict[str, Any])
|
@router.post("/roles", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def create_role(
|
def create_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
role: Role = Body(...),
|
role: Role = Body(...),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -928,7 +928,7 @@ async def create_role(
|
||||||
|
|
||||||
@router.get("/roles/{roleId}", response_model=Dict[str, Any])
|
@router.get("/roles/{roleId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_role(
|
def get_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
roleId: str = Path(..., description="Role ID"),
|
roleId: str = Path(..., description="Role ID"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -975,7 +975,7 @@ async def get_role(
|
||||||
|
|
||||||
@router.put("/roles/{roleId}", response_model=Dict[str, Any])
|
@router.put("/roles/{roleId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def update_role(
|
def update_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
roleId: str = Path(..., description="Role ID"),
|
roleId: str = Path(..., description="Role ID"),
|
||||||
role: Role = Body(...),
|
role: Role = Body(...),
|
||||||
|
|
@ -1028,7 +1028,7 @@ async def update_role(
|
||||||
|
|
||||||
@router.delete("/roles/{roleId}", response_model=Dict[str, str])
|
@router.delete("/roles/{roleId}", response_model=Dict[str, str])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def delete_role(
|
def delete_role(
|
||||||
request: Request,
|
request: Request,
|
||||||
roleId: str = Path(..., description="Role ID"),
|
roleId: str = Path(..., description="Role ID"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -1078,7 +1078,7 @@ async def delete_role(
|
||||||
|
|
||||||
@router.get("/catalog/objects", response_model=Dict[str, Any])
|
@router.get("/catalog/objects", response_model=Dict[str, Any])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def getCatalogObjects(
|
def getCatalogObjects(
|
||||||
request: Request,
|
request: Request,
|
||||||
context: Optional[str] = Query(None, description="Filter by context (DATA, UI, RESOURCE)"),
|
context: Optional[str] = Query(None, description="Filter by context (DATA, UI, RESOURCE)"),
|
||||||
featureCode: Optional[str] = Query(None, description="Filter by feature code"),
|
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])
|
@router.get("/catalog/stats", response_model=Dict[str, Any])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def getCatalogStats(
|
def getCatalogStats(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
|
@ -1200,7 +1200,7 @@ async def getCatalogStats(
|
||||||
|
|
||||||
@router.post("/cleanup/duplicate-rules", response_model=dict)
|
@router.post("/cleanup/duplicate-rules", response_model=dict)
|
||||||
@limiter.limit("5/minute")
|
@limiter.limit("5/minute")
|
||||||
async def cleanup_duplicate_access_rules(
|
def cleanup_duplicate_access_rules(
|
||||||
request: Request,
|
request: Request,
|
||||||
dryRun: bool = Query(True, description="If true, only report duplicates without deleting"),
|
dryRun: bool = Query(True, description="If true, only report duplicates without deleting"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ def _getRoleScopePriority(scope: str) -> int:
|
||||||
|
|
||||||
@router.get("/users", response_model=List[Dict[str, Any]])
|
@router.get("/users", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def listUsersForOverview(
|
def listUsersForOverview(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
|
|
@ -112,7 +112,7 @@ async def listUsersForOverview(
|
||||||
|
|
||||||
@router.get("/{userId}", response_model=Dict[str, Any])
|
@router.get("/{userId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def getUserAccessOverview(
|
def getUserAccessOverview(
|
||||||
request: Request,
|
request: Request,
|
||||||
userId: str = Path(..., description="User ID to get access overview for"),
|
userId: str = Path(..., description="User ID to get access overview for"),
|
||||||
mandateId: Optional[str] = Query(None, description="Filter by mandate ID"),
|
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])
|
@router.get("/{userId}/effective-permissions", response_model=Dict[str, Any])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def getEffectivePermissions(
|
def getEffectivePermissions(
|
||||||
request: Request,
|
request: Request,
|
||||||
userId: str = Path(..., description="User ID"),
|
userId: str = Path(..., description="User ID"),
|
||||||
mandateId: str = Query(..., description="Mandate ID context"),
|
mandateId: str = Query(..., description="Mandate ID context"),
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.get("/{entityType}", response_model=AttributeResponse)
|
@router.get("/{entityType}", response_model=AttributeResponse)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_entity_attributes(
|
def get_entity_attributes(
|
||||||
request: Request,
|
request: Request,
|
||||||
entityType: str = Path(..., description="Type of entity (e.g. prompt)")
|
entityType: str = Path(..., description="Type of entity (e.g. prompt)")
|
||||||
) -> AttributeResponse:
|
) -> AttributeResponse:
|
||||||
|
|
@ -76,7 +76,7 @@ async def get_entity_attributes(
|
||||||
|
|
||||||
@router.options("/{entityType}")
|
@router.options("/{entityType}")
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def options_entity_attributes(
|
def options_entity_attributes(
|
||||||
request: Request,
|
request: Request,
|
||||||
entityType: str = Path(..., description="Type of entity (e.g. prompt)")
|
entityType: str = Path(..., description="Type of entity (e.g. prompt)")
|
||||||
) -> Response:
|
) -> Response:
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,7 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.get("/balance", response_model=List[BillingBalanceResponse])
|
@router.get("/balance", response_model=List[BillingBalanceResponse])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def getBalance(
|
def getBalance(
|
||||||
request: Request,
|
request: Request,
|
||||||
ctx: RequestContext = Depends(getRequestContext)
|
ctx: RequestContext = Depends(getRequestContext)
|
||||||
):
|
):
|
||||||
|
|
@ -189,7 +189,7 @@ async def getBalance(
|
||||||
|
|
||||||
@router.get("/balance/{targetMandateId}", response_model=BillingBalanceResponse)
|
@router.get("/balance/{targetMandateId}", response_model=BillingBalanceResponse)
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def getBalanceForMandate(
|
def getBalanceForMandate(
|
||||||
request: Request,
|
request: Request,
|
||||||
targetMandateId: str = Path(..., description="Mandate ID"),
|
targetMandateId: str = Path(..., description="Mandate ID"),
|
||||||
ctx: RequestContext = Depends(getRequestContext)
|
ctx: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -230,7 +230,7 @@ async def getBalanceForMandate(
|
||||||
|
|
||||||
@router.get("/transactions", response_model=List[TransactionResponse])
|
@router.get("/transactions", response_model=List[TransactionResponse])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def getTransactions(
|
def getTransactions(
|
||||||
request: Request,
|
request: Request,
|
||||||
limit: int = Query(default=50, ge=1, le=500),
|
limit: int = Query(default=50, ge=1, le=500),
|
||||||
offset: int = Query(default=0, ge=0),
|
offset: int = Query(default=0, ge=0),
|
||||||
|
|
@ -276,7 +276,7 @@ async def getTransactions(
|
||||||
|
|
||||||
@router.get("/statistics/{period}", response_model=UsageReportResponse)
|
@router.get("/statistics/{period}", response_model=UsageReportResponse)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def getStatistics(
|
def getStatistics(
|
||||||
request: Request,
|
request: Request,
|
||||||
period: str = Path(..., description="Period: 'day', 'month', or 'year'"),
|
period: str = Path(..., description="Period: 'day', 'month', or 'year'"),
|
||||||
year: int = Query(..., description="Year"),
|
year: int = Query(..., description="Year"),
|
||||||
|
|
@ -361,7 +361,7 @@ async def getStatistics(
|
||||||
|
|
||||||
@router.get("/providers", response_model=List[str])
|
@router.get("/providers", response_model=List[str])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def getAllowedProviders(
|
def getAllowedProviders(
|
||||||
request: Request,
|
request: Request,
|
||||||
ctx: RequestContext = Depends(getRequestContext)
|
ctx: RequestContext = Depends(getRequestContext)
|
||||||
):
|
):
|
||||||
|
|
@ -388,7 +388,7 @@ async def getAllowedProviders(
|
||||||
|
|
||||||
@router.get("/admin/settings/{targetMandateId}", response_model=Dict[str, Any])
|
@router.get("/admin/settings/{targetMandateId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def getSettingsAdmin(
|
def getSettingsAdmin(
|
||||||
request: Request,
|
request: Request,
|
||||||
targetMandateId: str = Path(..., description="Mandate ID"),
|
targetMandateId: str = Path(..., description="Mandate ID"),
|
||||||
ctx: RequestContext = Depends(getRequestContext),
|
ctx: RequestContext = Depends(getRequestContext),
|
||||||
|
|
@ -415,7 +415,7 @@ async def getSettingsAdmin(
|
||||||
|
|
||||||
@router.post("/admin/settings/{targetMandateId}", response_model=Dict[str, Any])
|
@router.post("/admin/settings/{targetMandateId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def createOrUpdateSettings(
|
def createOrUpdateSettings(
|
||||||
request: Request,
|
request: Request,
|
||||||
targetMandateId: str = Path(..., description="Mandate ID"),
|
targetMandateId: str = Path(..., description="Mandate ID"),
|
||||||
settingsUpdate: BillingSettingsUpdate = Body(...),
|
settingsUpdate: BillingSettingsUpdate = Body(...),
|
||||||
|
|
@ -462,7 +462,7 @@ async def createOrUpdateSettings(
|
||||||
|
|
||||||
@router.post("/admin/credit/{targetMandateId}", response_model=Dict[str, Any])
|
@router.post("/admin/credit/{targetMandateId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def addCredit(
|
def addCredit(
|
||||||
request: Request,
|
request: Request,
|
||||||
targetMandateId: str = Path(..., description="Mandate ID"),
|
targetMandateId: str = Path(..., description="Mandate ID"),
|
||||||
creditRequest: CreditAddRequest = Body(...),
|
creditRequest: CreditAddRequest = Body(...),
|
||||||
|
|
@ -526,7 +526,7 @@ async def addCredit(
|
||||||
|
|
||||||
@router.get("/admin/accounts/{targetMandateId}", response_model=List[AccountSummary])
|
@router.get("/admin/accounts/{targetMandateId}", response_model=List[AccountSummary])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def getAccounts(
|
def getAccounts(
|
||||||
request: Request,
|
request: Request,
|
||||||
targetMandateId: str = Path(..., description="Mandate ID"),
|
targetMandateId: str = Path(..., description="Mandate ID"),
|
||||||
ctx: RequestContext = Depends(getRequestContext),
|
ctx: RequestContext = Depends(getRequestContext),
|
||||||
|
|
@ -572,7 +572,7 @@ class MandateUserSummary(BaseModel):
|
||||||
|
|
||||||
@router.get("/admin/users/{targetMandateId}", response_model=List[MandateUserSummary])
|
@router.get("/admin/users/{targetMandateId}", response_model=List[MandateUserSummary])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def getUsersForMandate(
|
def getUsersForMandate(
|
||||||
request: Request,
|
request: Request,
|
||||||
targetMandateId: str = Path(..., description="Mandate ID"),
|
targetMandateId: str = Path(..., description="Mandate ID"),
|
||||||
ctx: RequestContext = Depends(getRequestContext),
|
ctx: RequestContext = Depends(getRequestContext),
|
||||||
|
|
@ -627,7 +627,7 @@ async def getUsersForMandate(
|
||||||
|
|
||||||
@router.get("/admin/transactions/{targetMandateId}", response_model=List[TransactionResponse])
|
@router.get("/admin/transactions/{targetMandateId}", response_model=List[TransactionResponse])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def getTransactionsAdmin(
|
def getTransactionsAdmin(
|
||||||
request: Request,
|
request: Request,
|
||||||
targetMandateId: str = Path(..., description="Mandate ID"),
|
targetMandateId: str = Path(..., description="Mandate ID"),
|
||||||
limit: int = Query(default=100, ge=1, le=1000),
|
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])
|
@router.get("/view/mandates/balances", response_model=List[MandateBalanceResponse])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def getMandateViewBalances(
|
def getMandateViewBalances(
|
||||||
request: Request,
|
request: Request,
|
||||||
ctx: RequestContext = Depends(getRequestContext),
|
ctx: RequestContext = Depends(getRequestContext),
|
||||||
_admin = Depends(requireSysAdmin)
|
_admin = Depends(requireSysAdmin)
|
||||||
|
|
@ -691,7 +691,7 @@ async def getMandateViewBalances(
|
||||||
|
|
||||||
@router.get("/view/mandates/transactions", response_model=List[TransactionResponse])
|
@router.get("/view/mandates/transactions", response_model=List[TransactionResponse])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def getMandateViewTransactions(
|
def getMandateViewTransactions(
|
||||||
request: Request,
|
request: Request,
|
||||||
limit: int = Query(default=100, ge=1, le=1000),
|
limit: int = Query(default=100, ge=1, le=1000),
|
||||||
ctx: RequestContext = Depends(getRequestContext),
|
ctx: RequestContext = Depends(getRequestContext),
|
||||||
|
|
@ -734,7 +734,7 @@ async def getMandateViewTransactions(
|
||||||
|
|
||||||
@router.get("/view/users/balances", response_model=List[UserBalanceResponse])
|
@router.get("/view/users/balances", response_model=List[UserBalanceResponse])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def getUserViewBalances(
|
def getUserViewBalances(
|
||||||
request: Request,
|
request: Request,
|
||||||
ctx: RequestContext = Depends(getRequestContext)
|
ctx: RequestContext = Depends(getRequestContext)
|
||||||
):
|
):
|
||||||
|
|
@ -793,7 +793,7 @@ class ViewStatisticsResponse(BaseModel):
|
||||||
|
|
||||||
@router.get("/view/statistics")
|
@router.get("/view/statistics")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def getUserViewStatistics(
|
def getUserViewStatistics(
|
||||||
request: Request,
|
request: Request,
|
||||||
period: str = Query(default="month", description="Period: 'day' or 'month'"),
|
period: str = Query(default="month", description="Period: 'day' or 'month'"),
|
||||||
year: int = Query(default=None, description="Year"),
|
year: int = Query(default=None, description="Year"),
|
||||||
|
|
@ -962,7 +962,7 @@ async def getUserViewStatistics(
|
||||||
|
|
||||||
@router.get("/view/users/transactions", response_model=PaginatedResponse[UserTransactionResponse])
|
@router.get("/view/users/transactions", response_model=PaginatedResponse[UserTransactionResponse])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def getUserViewTransactions(
|
def getUserViewTransactions(
|
||||||
request: Request,
|
request: Request,
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
||||||
ctx: RequestContext = Depends(getRequestContext)
|
ctx: RequestContext = Depends(getRequestContext)
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.get("/statuses/options", response_model=List[Dict[str, Any]])
|
@router.get("/statuses/options", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_connection_status_options(
|
def get_connection_status_options(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
|
|
@ -100,7 +100,7 @@ async def get_connection_status_options(
|
||||||
|
|
||||||
@router.get("/authorities/options", response_model=List[Dict[str, Any]])
|
@router.get("/authorities/options", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_auth_authority_options(
|
def get_auth_authority_options(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
|
|
@ -288,7 +288,7 @@ async def get_connections(
|
||||||
|
|
||||||
@router.post("/", response_model=UserConnection)
|
@router.post("/", response_model=UserConnection)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_connection(
|
def create_connection(
|
||||||
request: Request,
|
request: Request,
|
||||||
connection_data: Dict[str, Any] = Body(...),
|
connection_data: Dict[str, Any] = Body(...),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -344,7 +344,7 @@ async def create_connection(
|
||||||
|
|
||||||
@router.put("/{connectionId}", response_model=UserConnection)
|
@router.put("/{connectionId}", response_model=UserConnection)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_connection(
|
def update_connection(
|
||||||
request: Request,
|
request: Request,
|
||||||
connectionId: str = Path(..., description="The ID of the connection to update"),
|
connectionId: str = Path(..., description="The ID of the connection to update"),
|
||||||
connection_data: Dict[str, Any] = Body(...),
|
connection_data: Dict[str, Any] = Body(...),
|
||||||
|
|
@ -416,7 +416,7 @@ async def update_connection(
|
||||||
|
|
||||||
@router.post("/{connectionId}/connect")
|
@router.post("/{connectionId}/connect")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def connect_service(
|
def connect_service(
|
||||||
request: Request,
|
request: Request,
|
||||||
connectionId: str = Path(..., description="The ID of the connection to connect"),
|
connectionId: str = Path(..., description="The ID of the connection to connect"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -482,7 +482,7 @@ async def connect_service(
|
||||||
|
|
||||||
@router.post("/{connectionId}/disconnect")
|
@router.post("/{connectionId}/disconnect")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def disconnect_service(
|
def disconnect_service(
|
||||||
request: Request,
|
request: Request,
|
||||||
connectionId: str = Path(..., description="The ID of the connection to disconnect"),
|
connectionId: str = Path(..., description="The ID of the connection to disconnect"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -532,7 +532,7 @@ async def disconnect_service(
|
||||||
|
|
||||||
@router.delete("/{connectionId}")
|
@router.delete("/{connectionId}")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_connection(
|
def delete_connection(
|
||||||
request: Request,
|
request: Request,
|
||||||
connectionId: str = Path(..., description="The ID of the connection to delete"),
|
connectionId: str = Path(..., description="The ID of the connection to delete"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.get("/list", response_model=PaginatedResponse[FileItem])
|
@router.get("/list", response_model=PaginatedResponse[FileItem])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_files(
|
def get_files(
|
||||||
request: Request,
|
request: Request,
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -168,7 +168,7 @@ async def upload_file(
|
||||||
|
|
||||||
@router.get("/{fileId}", response_model=FileItem)
|
@router.get("/{fileId}", response_model=FileItem)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_file(
|
def get_file(
|
||||||
request: Request,
|
request: Request,
|
||||||
fileId: str = Path(..., description="ID of the file"),
|
fileId: str = Path(..., description="ID of the file"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -214,7 +214,7 @@ async def get_file(
|
||||||
|
|
||||||
@router.put("/{fileId}", response_model=FileItem)
|
@router.put("/{fileId}", response_model=FileItem)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_file(
|
def update_file(
|
||||||
request: Request,
|
request: Request,
|
||||||
fileId: str = Path(..., description="ID of the file to update"),
|
fileId: str = Path(..., description="ID of the file to update"),
|
||||||
file_info: Dict[str, Any] = Body(...),
|
file_info: Dict[str, Any] = Body(...),
|
||||||
|
|
@ -262,7 +262,7 @@ async def update_file(
|
||||||
|
|
||||||
@router.delete("/{fileId}", response_model=Dict[str, Any])
|
@router.delete("/{fileId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_file(
|
def delete_file(
|
||||||
request: Request,
|
request: Request,
|
||||||
fileId: str = Path(..., description="ID of the file to delete"),
|
fileId: str = Path(..., description="ID of the file to delete"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -289,7 +289,7 @@ async def delete_file(
|
||||||
|
|
||||||
@router.get("/stats", response_model=Dict[str, Any])
|
@router.get("/stats", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_file_stats(
|
def get_file_stats(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
|
@ -327,7 +327,7 @@ async def get_file_stats(
|
||||||
|
|
||||||
@router.get("/{fileId}/download")
|
@router.get("/{fileId}/download")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def download_file(
|
def download_file(
|
||||||
request: Request,
|
request: Request,
|
||||||
fileId: str = Path(..., description="ID of the file to download"),
|
fileId: str = Path(..., description="ID of the file to download"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -375,7 +375,7 @@ async def download_file(
|
||||||
|
|
||||||
@router.get("/{fileId}/preview", response_model=FilePreview)
|
@router.get("/{fileId}/preview", response_model=FilePreview)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def preview_file(
|
def preview_file(
|
||||||
request: Request,
|
request: Request,
|
||||||
fileId: str = Path(..., description="ID of the file to preview"),
|
fileId: str = Path(..., description="ID of the file to preview"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.get("/", response_model=PaginatedResponse[Mandate])
|
@router.get("/", response_model=PaginatedResponse[Mandate])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_mandates(
|
def get_mandates(
|
||||||
request: Request,
|
request: Request,
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -140,7 +140,7 @@ async def get_mandates(
|
||||||
|
|
||||||
@router.get("/{mandateId}", response_model=Mandate)
|
@router.get("/{mandateId}", response_model=Mandate)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_mandate(
|
def get_mandate(
|
||||||
request: Request,
|
request: Request,
|
||||||
mandateId: str = Path(..., description="ID of the mandate"),
|
mandateId: str = Path(..., description="ID of the mandate"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -171,7 +171,7 @@ async def get_mandate(
|
||||||
|
|
||||||
@router.post("/", response_model=Mandate)
|
@router.post("/", response_model=Mandate)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_mandate(
|
def create_mandate(
|
||||||
request: Request,
|
request: Request,
|
||||||
mandateData: dict = Body(..., description="Mandate data with at least 'name' field"),
|
mandateData: dict = Body(..., description="Mandate data with at least 'name' field"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -224,7 +224,7 @@ async def create_mandate(
|
||||||
|
|
||||||
@router.put("/{mandateId}", response_model=Mandate)
|
@router.put("/{mandateId}", response_model=Mandate)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_mandate(
|
def update_mandate(
|
||||||
request: Request,
|
request: Request,
|
||||||
mandateId: str = Path(..., description="ID of the mandate to update"),
|
mandateId: str = Path(..., description="ID of the mandate to update"),
|
||||||
mandateData: dict = Body(..., description="Mandate update data"),
|
mandateData: dict = Body(..., description="Mandate update data"),
|
||||||
|
|
@ -270,7 +270,7 @@ async def update_mandate(
|
||||||
|
|
||||||
@router.delete("/{mandateId}", response_model=Dict[str, Any])
|
@router.delete("/{mandateId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_mandate(
|
def delete_mandate(
|
||||||
request: Request,
|
request: Request,
|
||||||
mandateId: str = Path(..., description="ID of the mandate to delete"),
|
mandateId: str = Path(..., description="ID of the mandate to delete"),
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -324,7 +324,7 @@ async def delete_mandate(
|
||||||
|
|
||||||
@router.get("/{targetMandateId}/users")
|
@router.get("/{targetMandateId}/users")
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def list_mandate_users(
|
def list_mandate_users(
|
||||||
request: Request,
|
request: Request,
|
||||||
targetMandateId: str = Path(..., description="ID of the mandate"),
|
targetMandateId: str = Path(..., description="ID of the mandate"),
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
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)
|
@router.post("/{targetMandateId}/users", response_model=UserMandateResponse)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def add_user_to_mandate(
|
def add_user_to_mandate(
|
||||||
request: Request,
|
request: Request,
|
||||||
targetMandateId: str = Path(..., description="ID of the mandate"),
|
targetMandateId: str = Path(..., description="ID of the mandate"),
|
||||||
data: UserMandateCreate = Body(...),
|
data: UserMandateCreate = Body(...),
|
||||||
|
|
@ -603,7 +603,7 @@ async def add_user_to_mandate(
|
||||||
|
|
||||||
@router.delete("/{targetMandateId}/users/{targetUserId}", response_model=Dict[str, str])
|
@router.delete("/{targetMandateId}/users/{targetUserId}", response_model=Dict[str, str])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def remove_user_from_mandate(
|
def remove_user_from_mandate(
|
||||||
request: Request,
|
request: Request,
|
||||||
targetMandateId: str = Path(..., description="ID of the mandate"),
|
targetMandateId: str = Path(..., description="ID of the mandate"),
|
||||||
targetUserId: str = Path(..., description="ID of the user to remove"),
|
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)
|
@router.put("/{targetMandateId}/users/{targetUserId}/roles", response_model=UserMandateResponse)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def update_user_roles_in_mandate(
|
def update_user_roles_in_mandate(
|
||||||
request: Request,
|
request: Request,
|
||||||
targetMandateId: str = Path(..., description="ID of the mandate"),
|
targetMandateId: str = Path(..., description="ID of the mandate"),
|
||||||
targetUserId: str = Path(..., description="ID of the user"),
|
targetUserId: str = Path(..., description="ID of the user"),
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.get("", response_model=PaginatedResponse[Prompt])
|
@router.get("", response_model=PaginatedResponse[Prompt])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_prompts(
|
def get_prompts(
|
||||||
request: Request,
|
request: Request,
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -83,7 +83,7 @@ async def get_prompts(
|
||||||
|
|
||||||
@router.post("", response_model=Prompt)
|
@router.post("", response_model=Prompt)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_prompt(
|
def create_prompt(
|
||||||
request: Request,
|
request: Request,
|
||||||
prompt: Prompt,
|
prompt: Prompt,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -98,7 +98,7 @@ async def create_prompt(
|
||||||
|
|
||||||
@router.get("/{promptId}", response_model=Prompt)
|
@router.get("/{promptId}", response_model=Prompt)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_prompt(
|
def get_prompt(
|
||||||
request: Request,
|
request: Request,
|
||||||
promptId: str = Path(..., description="ID of the prompt"),
|
promptId: str = Path(..., description="ID of the prompt"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -118,7 +118,7 @@ async def get_prompt(
|
||||||
|
|
||||||
@router.put("/{promptId}", response_model=Prompt)
|
@router.put("/{promptId}", response_model=Prompt)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_prompt(
|
def update_prompt(
|
||||||
request: Request,
|
request: Request,
|
||||||
promptId: str = Path(..., description="ID of the prompt to update"),
|
promptId: str = Path(..., description="ID of the prompt to update"),
|
||||||
promptData: Prompt = Body(...),
|
promptData: Prompt = Body(...),
|
||||||
|
|
@ -154,7 +154,7 @@ async def update_prompt(
|
||||||
|
|
||||||
@router.delete("/{promptId}", response_model=Dict[str, Any])
|
@router.delete("/{promptId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_prompt(
|
def delete_prompt(
|
||||||
request: Request,
|
request: Request,
|
||||||
promptId: str = Path(..., description="ID of the prompt to delete"),
|
promptId: str = Path(..., description="ID of the prompt to delete"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.get("/options", response_model=List[Dict[str, Any]])
|
@router.get("/options", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_user_options(
|
def get_user_options(
|
||||||
request: Request,
|
request: Request,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
|
|
@ -190,7 +190,7 @@ async def get_user_options(
|
||||||
|
|
||||||
@router.get("/", response_model=PaginatedResponse[User])
|
@router.get("/", response_model=PaginatedResponse[User])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_users(
|
def get_users(
|
||||||
request: Request,
|
request: Request,
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -304,7 +304,7 @@ async def get_users(
|
||||||
|
|
||||||
@router.get("/{userId}", response_model=User)
|
@router.get("/{userId}", response_model=User)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_user(
|
def get_user(
|
||||||
request: Request,
|
request: Request,
|
||||||
userId: str = Path(..., description="ID of the user"),
|
userId: str = Path(..., description="ID of the user"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -356,7 +356,7 @@ class CreateUserRequest(BaseModel):
|
||||||
|
|
||||||
@router.post("", response_model=User)
|
@router.post("", response_model=User)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def create_user(
|
def create_user(
|
||||||
request: Request,
|
request: Request,
|
||||||
userData: CreateUserRequest = Body(...),
|
userData: CreateUserRequest = Body(...),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -396,7 +396,7 @@ async def create_user(
|
||||||
|
|
||||||
@router.put("/{userId}", response_model=User)
|
@router.put("/{userId}", response_model=User)
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def update_user(
|
def update_user(
|
||||||
request: Request,
|
request: Request,
|
||||||
userId: str = Path(..., description="ID of the user to update"),
|
userId: str = Path(..., description="ID of the user to update"),
|
||||||
userData: User = Body(...),
|
userData: User = Body(...),
|
||||||
|
|
@ -438,7 +438,7 @@ async def update_user(
|
||||||
|
|
||||||
@router.post("/{userId}/reset-password")
|
@router.post("/{userId}/reset-password")
|
||||||
@limiter.limit("5/minute")
|
@limiter.limit("5/minute")
|
||||||
async def reset_user_password(
|
def reset_user_password(
|
||||||
request: Request,
|
request: Request,
|
||||||
userId: str = Path(..., description="ID of the user to reset password for"),
|
userId: str = Path(..., description="ID of the user to reset password for"),
|
||||||
newPassword: str = Body(..., embed=True),
|
newPassword: str = Body(..., embed=True),
|
||||||
|
|
@ -535,7 +535,7 @@ async def reset_user_password(
|
||||||
|
|
||||||
@router.post("/change-password")
|
@router.post("/change-password")
|
||||||
@limiter.limit("5/minute")
|
@limiter.limit("5/minute")
|
||||||
async def change_password(
|
def change_password(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentPassword: str = Body(..., embed=True),
|
currentPassword: str = Body(..., embed=True),
|
||||||
newPassword: str = Body(..., embed=True),
|
newPassword: str = Body(..., embed=True),
|
||||||
|
|
@ -614,7 +614,7 @@ async def change_password(
|
||||||
|
|
||||||
@router.post("/{userId}/send-password-link")
|
@router.post("/{userId}/send-password-link")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def send_password_link(
|
def send_password_link(
|
||||||
request: Request,
|
request: Request,
|
||||||
userId: str = Path(..., description="ID of the user to send password setup link"),
|
userId: str = Path(..., description="ID of the user to send password setup link"),
|
||||||
frontendUrl: str = Body(..., embed=True),
|
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])
|
@router.delete("/{userId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def delete_user(
|
def delete_user(
|
||||||
request: Request,
|
request: Request,
|
||||||
userId: str = Path(..., description="ID of the user to delete"),
|
userId: str = Path(..., description="ID of the user to delete"),
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ def getServiceChat(currentUser: User):
|
||||||
# Consolidated endpoint for getting all workflows
|
# Consolidated endpoint for getting all workflows
|
||||||
@router.get("/", response_model=PaginatedResponse[ChatWorkflow])
|
@router.get("/", response_model=PaginatedResponse[ChatWorkflow])
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def get_workflows(
|
def get_workflows(
|
||||||
request: Request,
|
request: Request,
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -123,7 +123,7 @@ async def get_workflows(
|
||||||
|
|
||||||
@router.get("/{workflowId}", response_model=ChatWorkflow)
|
@router.get("/{workflowId}", response_model=ChatWorkflow)
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def get_workflow(
|
def get_workflow(
|
||||||
request: Request,
|
request: Request,
|
||||||
workflowId: str = Path(..., description="ID of the workflow"),
|
workflowId: str = Path(..., description="ID of the workflow"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -152,7 +152,7 @@ async def get_workflow(
|
||||||
|
|
||||||
@router.put("/{workflowId}", response_model=ChatWorkflow)
|
@router.put("/{workflowId}", response_model=ChatWorkflow)
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def update_workflow(
|
def update_workflow(
|
||||||
request: Request,
|
request: Request,
|
||||||
workflowId: str = Path(..., description="ID of the workflow to update"),
|
workflowId: str = Path(..., description="ID of the workflow to update"),
|
||||||
workflowData: Dict[str, Any] = Body(...),
|
workflowData: Dict[str, Any] = Body(...),
|
||||||
|
|
@ -200,7 +200,7 @@ async def update_workflow(
|
||||||
# API Endpoint for workflow status
|
# API Endpoint for workflow status
|
||||||
@router.get("/{workflowId}/status", response_model=ChatWorkflow)
|
@router.get("/{workflowId}/status", response_model=ChatWorkflow)
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def get_workflow_status(
|
def get_workflow_status(
|
||||||
request: Request,
|
request: Request,
|
||||||
workflowId: str = Path(..., description="ID of the workflow"),
|
workflowId: str = Path(..., description="ID of the workflow"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -274,7 +274,7 @@ async def stop_workflow(
|
||||||
# API Endpoint for workflow logs with selective data transfer
|
# API Endpoint for workflow logs with selective data transfer
|
||||||
@router.get("/{workflowId}/logs", response_model=PaginatedResponse[ChatLog])
|
@router.get("/{workflowId}/logs", response_model=PaginatedResponse[ChatLog])
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def get_workflow_logs(
|
def get_workflow_logs(
|
||||||
request: Request,
|
request: Request,
|
||||||
workflowId: str = Path(..., description="ID of the workflow"),
|
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)"),
|
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
|
# API Endpoint for workflow messages with selective data transfer
|
||||||
@router.get("/{workflowId}/messages", response_model=PaginatedResponse[ChatMessage])
|
@router.get("/{workflowId}/messages", response_model=PaginatedResponse[ChatMessage])
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def get_workflow_messages(
|
def get_workflow_messages(
|
||||||
request: Request,
|
request: Request,
|
||||||
workflowId: str = Path(..., description="ID of the workflow"),
|
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)"),
|
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
|
# State 11: Workflow Reset/Deletion endpoint
|
||||||
@router.delete("/{workflowId}", response_model=Dict[str, Any])
|
@router.delete("/{workflowId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def delete_workflow(
|
def delete_workflow(
|
||||||
request: Request,
|
request: Request,
|
||||||
workflowId: str = Path(..., description="ID of the workflow to delete"),
|
workflowId: str = Path(..., description="ID of the workflow to delete"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -516,7 +516,7 @@ async def delete_workflow(
|
||||||
|
|
||||||
@router.delete("/{workflowId}/messages/{messageId}", response_model=Dict[str, Any])
|
@router.delete("/{workflowId}/messages/{messageId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def delete_workflow_message(
|
def delete_workflow_message(
|
||||||
request: Request,
|
request: Request,
|
||||||
workflowId: str = Path(..., description="ID of the workflow"),
|
workflowId: str = Path(..., description="ID of the workflow"),
|
||||||
messageId: str = Path(..., description="ID of the message to delete"),
|
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])
|
@router.delete("/{workflowId}/messages/{messageId}/files/{fileId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def delete_file_from_message(
|
def delete_file_from_message(
|
||||||
request: Request,
|
request: Request,
|
||||||
workflowId: str = Path(..., description="ID of the workflow"),
|
workflowId: str = Path(..., description="ID of the workflow"),
|
||||||
messageId: str = Path(..., description="ID of the message"),
|
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])
|
@router.get("/actions", response_model=Dict[str, Any])
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def get_all_actions(
|
def get_all_actions(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
|
@ -685,7 +685,7 @@ async def get_all_actions(
|
||||||
|
|
||||||
@router.get("/actions/{method}", response_model=Dict[str, Any])
|
@router.get("/actions/{method}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def get_method_actions(
|
def get_method_actions(
|
||||||
request: Request,
|
request: Request,
|
||||||
method: str = Path(..., description="Method name (e.g., 'outlook', 'sharepoint')"),
|
method: str = Path(..., description="Method name (e.g., 'outlook', 'sharepoint')"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -768,7 +768,7 @@ async def get_method_actions(
|
||||||
|
|
||||||
@router.get("/actions/{method}/{action}", response_model=Dict[str, Any])
|
@router.get("/actions/{method}/{action}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def get_action_schema(
|
def get_action_schema(
|
||||||
request: Request,
|
request: Request,
|
||||||
method: str = Path(..., description="Method name (e.g., 'outlook', 'sharepoint')"),
|
method: str = Path(..., description="Method name (e.g., 'outlook', 'sharepoint')"),
|
||||||
action: str = Path(..., description="Action name (e.g., 'readEmails', 'uploadDocument')"),
|
action: str = Path(..., description="Action name (e.g., 'readEmails', 'uploadDocument')"),
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ class DeletionResult(BaseModel):
|
||||||
|
|
||||||
@router.get("/data-export", response_model=DataExportResponse)
|
@router.get("/data-export", response_model=DataExportResponse)
|
||||||
@limiter.limit("5/minute")
|
@limiter.limit("5/minute")
|
||||||
async def export_user_data(
|
def export_user_data(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> DataExportResponse:
|
) -> DataExportResponse:
|
||||||
|
|
@ -215,7 +215,7 @@ async def export_user_data(
|
||||||
|
|
||||||
@router.get("/data-portability")
|
@router.get("/data-portability")
|
||||||
@limiter.limit("5/minute")
|
@limiter.limit("5/minute")
|
||||||
async def export_portable_data(
|
def export_portable_data(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> JSONResponse:
|
) -> JSONResponse:
|
||||||
|
|
@ -296,7 +296,7 @@ async def export_portable_data(
|
||||||
|
|
||||||
@router.delete("/", response_model=DeletionResult)
|
@router.delete("/", response_model=DeletionResult)
|
||||||
@limiter.limit("1/hour")
|
@limiter.limit("1/hour")
|
||||||
async def delete_account(
|
def delete_account(
|
||||||
request: Request,
|
request: Request,
|
||||||
confirmDeletion: bool = False,
|
confirmDeletion: bool = False,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -391,7 +391,7 @@ async def delete_account(
|
||||||
|
|
||||||
@router.get("/consent-info", response_model=Dict[str, Any])
|
@router.get("/consent-info", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_consent_info(
|
def get_consent_info(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ class InvitationValidation(BaseModel):
|
||||||
|
|
||||||
@router.post("/", response_model=InvitationResponse)
|
@router.post("/", response_model=InvitationResponse)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def create_invitation(
|
def create_invitation(
|
||||||
request: Request,
|
request: Request,
|
||||||
data: InvitationCreate,
|
data: InvitationCreate,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -300,7 +300,7 @@ async def create_invitation(
|
||||||
|
|
||||||
@router.get("/", response_model=List[Dict[str, Any]])
|
@router.get("/", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def list_invitations(
|
def list_invitations(
|
||||||
request: Request,
|
request: Request,
|
||||||
includeUsed: bool = Query(False, description="Include already used invitations"),
|
includeUsed: bool = Query(False, description="Include already used invitations"),
|
||||||
includeExpired: bool = Query(False, description="Include expired 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])
|
@router.delete("/{invitationId}", response_model=Dict[str, str])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def revoke_invitation(
|
def revoke_invitation(
|
||||||
request: Request,
|
request: Request,
|
||||||
invitationId: str,
|
invitationId: str,
|
||||||
context: RequestContext = Depends(getRequestContext)
|
context: RequestContext = Depends(getRequestContext)
|
||||||
|
|
@ -458,7 +458,7 @@ async def revoke_invitation(
|
||||||
|
|
||||||
@router.get("/validate/{token}", response_model=InvitationValidation)
|
@router.get("/validate/{token}", response_model=InvitationValidation)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def validate_invitation(
|
def validate_invitation(
|
||||||
request: Request,
|
request: Request,
|
||||||
token: str
|
token: str
|
||||||
) -> InvitationValidation:
|
) -> InvitationValidation:
|
||||||
|
|
@ -562,7 +562,7 @@ async def validate_invitation(
|
||||||
|
|
||||||
@router.post("/accept/{token}", response_model=Dict[str, Any])
|
@router.post("/accept/{token}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def accept_invitation(
|
def accept_invitation(
|
||||||
request: Request,
|
request: Request,
|
||||||
token: str,
|
token: str,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.get("/subscriptions", response_model=PaginatedResponse[MessagingSubscription])
|
@router.get("/subscriptions", response_model=PaginatedResponse[MessagingSubscription])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_subscriptions(
|
def get_subscriptions(
|
||||||
request: Request,
|
request: Request,
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -79,7 +79,7 @@ async def get_subscriptions(
|
||||||
|
|
||||||
@router.post("/subscriptions", response_model=MessagingSubscription)
|
@router.post("/subscriptions", response_model=MessagingSubscription)
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def create_subscription(
|
def create_subscription(
|
||||||
request: Request,
|
request: Request,
|
||||||
subscription: MessagingSubscription,
|
subscription: MessagingSubscription,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -95,7 +95,7 @@ async def create_subscription(
|
||||||
|
|
||||||
@router.get("/subscriptions/{subscriptionId}", response_model=MessagingSubscription)
|
@router.get("/subscriptions/{subscriptionId}", response_model=MessagingSubscription)
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_subscription(
|
def get_subscription(
|
||||||
request: Request,
|
request: Request,
|
||||||
subscriptionId: str = Path(..., description="ID of the subscription"),
|
subscriptionId: str = Path(..., description="ID of the subscription"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -115,7 +115,7 @@ async def get_subscription(
|
||||||
|
|
||||||
@router.put("/subscriptions/{subscriptionId}", response_model=MessagingSubscription)
|
@router.put("/subscriptions/{subscriptionId}", response_model=MessagingSubscription)
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def update_subscription(
|
def update_subscription(
|
||||||
request: Request,
|
request: Request,
|
||||||
subscriptionId: str = Path(..., description="ID of the subscription to update"),
|
subscriptionId: str = Path(..., description="ID of the subscription to update"),
|
||||||
subscriptionData: MessagingSubscription = Body(...),
|
subscriptionData: MessagingSubscription = Body(...),
|
||||||
|
|
@ -145,7 +145,7 @@ async def update_subscription(
|
||||||
|
|
||||||
@router.delete("/subscriptions/{subscriptionId}", response_model=Dict[str, Any])
|
@router.delete("/subscriptions/{subscriptionId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def delete_subscription(
|
def delete_subscription(
|
||||||
request: Request,
|
request: Request,
|
||||||
subscriptionId: str = Path(..., description="ID of the subscription to delete"),
|
subscriptionId: str = Path(..., description="ID of the subscription to delete"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -174,7 +174,7 @@ async def delete_subscription(
|
||||||
|
|
||||||
@router.get("/subscriptions/{subscriptionId}/registrations", response_model=PaginatedResponse[MessagingSubscriptionRegistration])
|
@router.get("/subscriptions/{subscriptionId}/registrations", response_model=PaginatedResponse[MessagingSubscriptionRegistration])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_subscription_registrations(
|
def get_subscription_registrations(
|
||||||
request: Request,
|
request: Request,
|
||||||
subscriptionId: str = Path(..., description="ID of the subscription"),
|
subscriptionId: str = Path(..., description="ID of the subscription"),
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
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)
|
@router.post("/subscriptions/{subscriptionId}/subscribe", response_model=MessagingSubscriptionRegistration)
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def subscribe_user(
|
def subscribe_user(
|
||||||
request: Request,
|
request: Request,
|
||||||
subscriptionId: str = Path(..., description="ID of the subscription"),
|
subscriptionId: str = Path(..., description="ID of the subscription"),
|
||||||
channel: MessagingChannel = Body(..., embed=True),
|
channel: MessagingChannel = Body(..., embed=True),
|
||||||
|
|
@ -241,7 +241,7 @@ async def subscribe_user(
|
||||||
|
|
||||||
@router.delete("/subscriptions/{subscriptionId}/unsubscribe", response_model=Dict[str, Any])
|
@router.delete("/subscriptions/{subscriptionId}/unsubscribe", response_model=Dict[str, Any])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def unsubscribe_user(
|
def unsubscribe_user(
|
||||||
request: Request,
|
request: Request,
|
||||||
subscriptionId: str = Path(..., description="ID of the subscription"),
|
subscriptionId: str = Path(..., description="ID of the subscription"),
|
||||||
channel: MessagingChannel = Body(..., embed=True),
|
channel: MessagingChannel = Body(..., embed=True),
|
||||||
|
|
@ -267,7 +267,7 @@ async def unsubscribe_user(
|
||||||
|
|
||||||
@router.get("/registrations", response_model=PaginatedResponse[MessagingSubscriptionRegistration])
|
@router.get("/registrations", response_model=PaginatedResponse[MessagingSubscriptionRegistration])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_my_registrations(
|
def get_my_registrations(
|
||||||
request: Request,
|
request: Request,
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -311,7 +311,7 @@ async def get_my_registrations(
|
||||||
|
|
||||||
@router.put("/registrations/{registrationId}", response_model=MessagingSubscriptionRegistration)
|
@router.put("/registrations/{registrationId}", response_model=MessagingSubscriptionRegistration)
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def update_registration(
|
def update_registration(
|
||||||
request: Request,
|
request: Request,
|
||||||
registrationId: str = Path(..., description="ID of the registration to update"),
|
registrationId: str = Path(..., description="ID of the registration to update"),
|
||||||
registrationData: MessagingSubscriptionRegistration = Body(...),
|
registrationData: MessagingSubscriptionRegistration = Body(...),
|
||||||
|
|
@ -341,7 +341,7 @@ async def update_registration(
|
||||||
|
|
||||||
@router.delete("/registrations/{registrationId}", response_model=Dict[str, Any])
|
@router.delete("/registrations/{registrationId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def delete_registration(
|
def delete_registration(
|
||||||
request: Request,
|
request: Request,
|
||||||
registrationId: str = Path(..., description="ID of the registration to delete"),
|
registrationId: str = Path(..., description="ID of the registration to delete"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -376,7 +376,7 @@ def _getTriggerKey(request: Request) -> str:
|
||||||
|
|
||||||
@router.post("/trigger/{subscriptionId}", response_model=MessagingSubscriptionExecutionResult)
|
@router.post("/trigger/{subscriptionId}", response_model=MessagingSubscriptionExecutionResult)
|
||||||
@limiter.limit("60/minute", key_func=_getTriggerKey)
|
@limiter.limit("60/minute", key_func=_getTriggerKey)
|
||||||
async def trigger_subscription(
|
def trigger_subscription(
|
||||||
request: Request,
|
request: Request,
|
||||||
subscriptionId: str = Path(..., description="ID of the subscription to trigger"),
|
subscriptionId: str = Path(..., description="ID of the subscription to trigger"),
|
||||||
eventParameters: Dict[str, Any] = Body(...),
|
eventParameters: Dict[str, Any] = Body(...),
|
||||||
|
|
@ -439,7 +439,7 @@ def _hasTriggerPermission(context: RequestContext) -> bool:
|
||||||
|
|
||||||
@router.get("/deliveries", response_model=PaginatedResponse[MessagingDelivery])
|
@router.get("/deliveries", response_model=PaginatedResponse[MessagingDelivery])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_deliveries(
|
def get_deliveries(
|
||||||
request: Request,
|
request: Request,
|
||||||
subscriptionId: Optional[str] = Query(None, description="Filter by subscription ID"),
|
subscriptionId: Optional[str] = Query(None, description="Filter by subscription ID"),
|
||||||
pagination: Optional[str] = Query(None, description="JSON-encoded PaginationParams object"),
|
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)
|
@router.get("/deliveries/{deliveryId}", response_model=MessagingDelivery)
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_delivery(
|
def get_delivery(
|
||||||
request: Request,
|
request: Request,
|
||||||
deliveryId: str = Path(..., description="ID of the delivery"),
|
deliveryId: str = Path(..., description="ID of the delivery"),
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ def createInvitationNotification(
|
||||||
|
|
||||||
@router.get("", response_model=List[Dict[str, Any]])
|
@router.get("", response_model=List[Dict[str, Any]])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def getNotifications(
|
def getNotifications(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser),
|
currentUser: User = Depends(getCurrentUser),
|
||||||
status: Optional[str] = None,
|
status: Optional[str] = None,
|
||||||
|
|
@ -161,7 +161,7 @@ async def getNotifications(
|
||||||
|
|
||||||
@router.get("/unread-count", response_model=UnreadCountResponse)
|
@router.get("/unread-count", response_model=UnreadCountResponse)
|
||||||
@limiter.limit("120/minute")
|
@limiter.limit("120/minute")
|
||||||
async def getUnreadCount(
|
def getUnreadCount(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> UnreadCountResponse:
|
) -> UnreadCountResponse:
|
||||||
|
|
@ -190,7 +190,7 @@ async def getUnreadCount(
|
||||||
|
|
||||||
@router.put("/{notificationId}/read", response_model=Dict[str, Any])
|
@router.put("/{notificationId}/read", response_model=Dict[str, Any])
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def markAsRead(
|
def markAsRead(
|
||||||
request: Request,
|
request: Request,
|
||||||
notificationId: str,
|
notificationId: str,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
@ -241,7 +241,7 @@ async def markAsRead(
|
||||||
|
|
||||||
@router.put("/mark-all-read", response_model=Dict[str, Any])
|
@router.put("/mark-all-read", response_model=Dict[str, Any])
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def markAllAsRead(
|
def markAllAsRead(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
|
@ -283,7 +283,7 @@ async def markAllAsRead(
|
||||||
|
|
||||||
@router.post("/{notificationId}/action", response_model=Dict[str, Any])
|
@router.post("/{notificationId}/action", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def executeAction(
|
def executeAction(
|
||||||
request: Request,
|
request: Request,
|
||||||
notificationId: str,
|
notificationId: str,
|
||||||
actionRequest: NotificationActionRequest,
|
actionRequest: NotificationActionRequest,
|
||||||
|
|
@ -332,7 +332,7 @@ async def executeAction(
|
||||||
actionResult = None
|
actionResult = None
|
||||||
|
|
||||||
if notification.get("type") == NotificationType.INVITATION.value:
|
if notification.get("type") == NotificationType.INVITATION.value:
|
||||||
actionResult = await _handleInvitationAction(
|
actionResult = _handleInvitationAction(
|
||||||
notification=notification,
|
notification=notification,
|
||||||
actionId=actionRequest.actionId,
|
actionId=actionRequest.actionId,
|
||||||
currentUser=currentUser,
|
currentUser=currentUser,
|
||||||
|
|
@ -370,7 +370,7 @@ async def executeAction(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _handleInvitationAction(
|
def _handleInvitationAction(
|
||||||
notification: Dict[str, Any],
|
notification: Dict[str, Any],
|
||||||
actionId: str,
|
actionId: str,
|
||||||
currentUser: User,
|
currentUser: User,
|
||||||
|
|
@ -488,7 +488,7 @@ async def _handleInvitationAction(
|
||||||
|
|
||||||
@router.delete("/{notificationId}", response_model=Dict[str, Any])
|
@router.delete("/{notificationId}", response_model=Dict[str, Any])
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def deleteNotification(
|
def deleteNotification(
|
||||||
request: Request,
|
request: Request,
|
||||||
notificationId: str,
|
notificationId: str,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ def _getDatabaseConnector(databaseName: str, userId: str = None) -> DatabaseConn
|
||||||
|
|
||||||
@router.get("/tokens")
|
@router.get("/tokens")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def list_tokens(
|
def list_tokens(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(requireSysAdmin),
|
currentUser: User = Depends(requireSysAdmin),
|
||||||
userId: Optional[str] = None,
|
userId: Optional[str] = None,
|
||||||
|
|
@ -137,7 +137,7 @@ async def list_tokens(
|
||||||
|
|
||||||
@router.post("/tokens/revoke/user")
|
@router.post("/tokens/revoke/user")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def revoke_tokens_by_user(
|
def revoke_tokens_by_user(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(requireSysAdmin),
|
currentUser: User = Depends(requireSysAdmin),
|
||||||
payload: Dict[str, Any] = Body(...)
|
payload: Dict[str, Any] = Body(...)
|
||||||
|
|
@ -172,7 +172,7 @@ async def revoke_tokens_by_user(
|
||||||
|
|
||||||
@router.post("/tokens/revoke/session")
|
@router.post("/tokens/revoke/session")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def revoke_tokens_by_session(
|
def revoke_tokens_by_session(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(requireSysAdmin),
|
currentUser: User = Depends(requireSysAdmin),
|
||||||
payload: Dict[str, Any] = Body(...)
|
payload: Dict[str, Any] = Body(...)
|
||||||
|
|
@ -208,7 +208,7 @@ async def revoke_tokens_by_session(
|
||||||
|
|
||||||
@router.post("/tokens/revoke/id")
|
@router.post("/tokens/revoke/id")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def revoke_token_by_id(
|
def revoke_token_by_id(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(requireSysAdmin),
|
currentUser: User = Depends(requireSysAdmin),
|
||||||
payload: Dict[str, Any] = Body(...)
|
payload: Dict[str, Any] = Body(...)
|
||||||
|
|
@ -235,7 +235,7 @@ async def revoke_token_by_id(
|
||||||
|
|
||||||
@router.post("/tokens/revoke/mandate")
|
@router.post("/tokens/revoke/mandate")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def revoke_tokens_by_mandate(
|
def revoke_tokens_by_mandate(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(requireSysAdmin),
|
currentUser: User = Depends(requireSysAdmin),
|
||||||
payload: Dict[str, Any] = Body(...)
|
payload: Dict[str, Any] = Body(...)
|
||||||
|
|
@ -280,7 +280,7 @@ async def revoke_tokens_by_mandate(
|
||||||
|
|
||||||
@router.get("/logs/{log_name}")
|
@router.get("/logs/{log_name}")
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def download_log(
|
def download_log(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(requireSysAdmin),
|
currentUser: User = Depends(requireSysAdmin),
|
||||||
log_name: str = "poweron"
|
log_name: str = "poweron"
|
||||||
|
|
@ -309,7 +309,7 @@ async def download_log(
|
||||||
|
|
||||||
@router.get("/databases")
|
@router.get("/databases")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def list_databases(
|
def list_databases(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
|
@ -327,7 +327,7 @@ async def list_databases(
|
||||||
|
|
||||||
@router.get("/databases/{database_name}/tables")
|
@router.get("/databases/{database_name}/tables")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_database_tables(
|
def get_database_tables(
|
||||||
request: Request,
|
request: Request,
|
||||||
database_name: str,
|
database_name: str,
|
||||||
currentUser: User = Depends(requireSysAdmin)
|
currentUser: User = Depends(requireSysAdmin)
|
||||||
|
|
@ -356,7 +356,7 @@ async def get_database_tables(
|
||||||
|
|
||||||
@router.post("/databases/{database_name}/tables/{table_name}/drop")
|
@router.post("/databases/{database_name}/tables/{table_name}/drop")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def drop_table(
|
def drop_table(
|
||||||
request: Request,
|
request: Request,
|
||||||
database_name: str,
|
database_name: str,
|
||||||
table_name: str,
|
table_name: str,
|
||||||
|
|
@ -404,7 +404,7 @@ async def drop_table(
|
||||||
|
|
||||||
@router.post("/databases/drop")
|
@router.post("/databases/drop")
|
||||||
@limiter.limit("5/minute")
|
@limiter.limit("5/minute")
|
||||||
async def drop_database(
|
def drop_database(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(requireSysAdmin),
|
currentUser: User = Depends(requireSysAdmin),
|
||||||
payload: Dict[str, Any] = Body(...)
|
payload: Dict[str, Any] = Body(...)
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ SCOPES = [
|
||||||
]
|
]
|
||||||
|
|
||||||
@router.get("/config")
|
@router.get("/config")
|
||||||
async def get_config():
|
def get_config():
|
||||||
"""Debug endpoint to check Google OAuth configuration"""
|
"""Debug endpoint to check Google OAuth configuration"""
|
||||||
return {
|
return {
|
||||||
"client_id": CLIENT_ID,
|
"client_id": CLIENT_ID,
|
||||||
|
|
@ -109,7 +109,7 @@ async def get_config():
|
||||||
|
|
||||||
@router.get("/login")
|
@router.get("/login")
|
||||||
@limiter.limit("5/minute")
|
@limiter.limit("5/minute")
|
||||||
async def login(
|
def login(
|
||||||
request: Request,
|
request: Request,
|
||||||
state: str = Query("login", description="State parameter to distinguish between login and connection flows"),
|
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")
|
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)
|
@router.get("/me", response_model=User)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_current_user(
|
def get_current_user(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> User:
|
) -> User:
|
||||||
|
|
@ -605,7 +605,7 @@ async def get_current_user(
|
||||||
|
|
||||||
@router.post("/logout")
|
@router.post("/logout")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def logout(
|
def logout(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.post("/login")
|
@router.post("/login")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def login(
|
def login(
|
||||||
request: Request,
|
request: Request,
|
||||||
response: Response,
|
response: Response,
|
||||||
formData: OAuth2PasswordRequestForm = Depends(),
|
formData: OAuth2PasswordRequestForm = Depends(),
|
||||||
|
|
@ -242,7 +242,7 @@ async def login(
|
||||||
|
|
||||||
@router.post("/register")
|
@router.post("/register")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def register_user(
|
def register_user(
|
||||||
request: Request,
|
request: Request,
|
||||||
userData: User = Body(...),
|
userData: User = Body(...),
|
||||||
frontendUrl: str = Body(..., embed=True)
|
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)
|
@router.get("/me", response_model=User)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def read_user_me(
|
def read_user_me(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> User:
|
) -> User:
|
||||||
|
|
@ -397,7 +397,7 @@ async def read_user_me(
|
||||||
|
|
||||||
@router.post("/refresh")
|
@router.post("/refresh")
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def refresh_token(
|
def refresh_token(
|
||||||
request: Request,
|
request: Request,
|
||||||
response: Response
|
response: Response
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
|
@ -472,7 +472,7 @@ async def refresh_token(
|
||||||
|
|
||||||
@router.post("/logout")
|
@router.post("/logout")
|
||||||
@limiter.limit("30/minute")
|
@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"""
|
"""Logout from local authentication"""
|
||||||
try:
|
try:
|
||||||
# Get user interface with current user context
|
# Get user interface with current user context
|
||||||
|
|
@ -541,7 +541,7 @@ async def logout(request: Request, response: Response, currentUser: User = Depen
|
||||||
|
|
||||||
@router.get("/available")
|
@router.get("/available")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def check_username_availability(
|
def check_username_availability(
|
||||||
request: Request,
|
request: Request,
|
||||||
username: str,
|
username: str,
|
||||||
authenticationAuthority: str = "local"
|
authenticationAuthority: str = "local"
|
||||||
|
|
@ -573,7 +573,7 @@ async def check_username_availability(
|
||||||
|
|
||||||
@router.post("/password-reset-request")
|
@router.post("/password-reset-request")
|
||||||
@limiter.limit("5/minute")
|
@limiter.limit("5/minute")
|
||||||
async def password_reset_request(
|
def password_reset_request(
|
||||||
request: Request,
|
request: Request,
|
||||||
username: str = Body(..., embed=True),
|
username: str = Body(..., embed=True),
|
||||||
frontendUrl: 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")
|
@router.post("/password-reset")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def password_reset(
|
def password_reset(
|
||||||
request: Request,
|
request: Request,
|
||||||
token: str = Body(..., embed=True),
|
token: str = Body(..., embed=True),
|
||||||
password: str = Body(..., embed=True)
|
password: str = Body(..., embed=True)
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ SCOPES = [
|
||||||
|
|
||||||
@router.get("/login")
|
@router.get("/login")
|
||||||
@limiter.limit("5/minute")
|
@limiter.limit("5/minute")
|
||||||
async def login(
|
def login(
|
||||||
request: Request,
|
request: Request,
|
||||||
state: str = Query("login", description="State parameter to distinguish between login and connection flows"),
|
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")
|
connectionId: Optional[str] = Query(None, description="Connection ID for connection flow")
|
||||||
|
|
@ -138,7 +138,7 @@ async def login(
|
||||||
|
|
||||||
@router.get("/adminconsent")
|
@router.get("/adminconsent")
|
||||||
@limiter.limit("5/minute")
|
@limiter.limit("5/minute")
|
||||||
async def adminconsent(request: Request) -> RedirectResponse:
|
def adminconsent(request: Request) -> RedirectResponse:
|
||||||
"""Initiate Microsoft Admin Consent flow.
|
"""Initiate Microsoft Admin Consent flow.
|
||||||
|
|
||||||
An Azure AD admin must visit this URL once to grant consent for the entire tenant.
|
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")
|
@router.get("/adminconsent/callback")
|
||||||
async def adminconsent_callback(
|
def adminconsent_callback(
|
||||||
admin_consent: Optional[str] = Query(None),
|
admin_consent: Optional[str] = Query(None),
|
||||||
tenant: Optional[str] = Query(None),
|
tenant: Optional[str] = Query(None),
|
||||||
error: 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)
|
@router.get("/me", response_model=User)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_current_user(
|
def get_current_user(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> User:
|
) -> User:
|
||||||
|
|
@ -619,7 +619,7 @@ async def get_current_user(
|
||||||
|
|
||||||
@router.post("/logout")
|
@router.post("/logout")
|
||||||
@limiter.limit("10/minute")
|
@limiter.limit("10/minute")
|
||||||
async def logout(
|
def logout(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
|
@ -655,7 +655,7 @@ async def logout(
|
||||||
|
|
||||||
@router.post("/cleanup")
|
@router.post("/cleanup")
|
||||||
@limiter.limit("5/minute")
|
@limiter.limit("5/minute")
|
||||||
async def cleanup_expired_tokens(
|
def cleanup_expired_tokens(
|
||||||
request: Request,
|
request: Request,
|
||||||
currentUser: User = Depends(getCurrentUser)
|
currentUser: User = Depends(getCurrentUser)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
|
|
||||||
|
|
@ -409,7 +409,7 @@ def _formatBlockItem(item: Dict[str, Any], language: str) -> Dict[str, Any]:
|
||||||
|
|
||||||
@navigationRouter.get("/navigation")
|
@navigationRouter.get("/navigation")
|
||||||
@limiter.limit("60/minute")
|
@limiter.limit("60/minute")
|
||||||
async def get_navigation(
|
def get_navigation(
|
||||||
request: Request,
|
request: Request,
|
||||||
language: str = Query("de", description="Language for labels (en, de, fr)"),
|
language: str = Query("de", description="Language for labels (en, de, fr)"),
|
||||||
reqContext: RequestContext = Depends(getRequestContext)
|
reqContext: RequestContext = Depends(getRequestContext)
|
||||||
|
|
|
||||||
|
|
@ -177,17 +177,14 @@ async def executeAutomation(automationId: str, services) -> ChatWorkflow:
|
||||||
workflow = services.interfaceDbChat.updateWorkflow(workflow.id, {"name": workflowName})
|
workflow = services.interfaceDbChat.updateWorkflow(workflow.id, {"name": workflowName})
|
||||||
logger.info(f"Set workflow {workflow.id} name to: {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 = list(automation.executionLogs or [])
|
||||||
executionLogs.append(executionLog)
|
executionLogs.append(executionLog)
|
||||||
# Keep only last 50 executions
|
# Keep only last 50 executions
|
||||||
if len(executionLogs) > 50:
|
if len(executionLogs) > 50:
|
||||||
executionLogs = executionLogs[-50:]
|
executionLogs = executionLogs[-50:]
|
||||||
|
|
||||||
services.interfaceDbAutomation.updateAutomationDefinition(
|
services.interfaceDbAutomation._saveExecutionLog(automationId, executionLogs)
|
||||||
automationId,
|
|
||||||
{"executionLogs": executionLogs}
|
|
||||||
)
|
|
||||||
|
|
||||||
return workflow
|
return workflow
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -195,7 +192,7 @@ async def executeAutomation(automationId: str, services) -> ChatWorkflow:
|
||||||
executionLog["status"] = "error"
|
executionLog["status"] = "error"
|
||||||
executionLog["messages"].append(f"Error: {str(e)}")
|
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:
|
try:
|
||||||
automation = services.interfaceDbAutomation.getAutomationDefinition(automationId)
|
automation = services.interfaceDbAutomation.getAutomationDefinition(automationId)
|
||||||
if automation:
|
if automation:
|
||||||
|
|
@ -203,10 +200,7 @@ async def executeAutomation(automationId: str, services) -> ChatWorkflow:
|
||||||
executionLogs.append(executionLog)
|
executionLogs.append(executionLog)
|
||||||
if len(executionLogs) > 50:
|
if len(executionLogs) > 50:
|
||||||
executionLogs = executionLogs[-50:]
|
executionLogs = executionLogs[-50:]
|
||||||
services.interfaceDbAutomation.updateAutomationDefinition(
|
services.interfaceDbAutomation._saveExecutionLog(automationId, executionLogs)
|
||||||
automationId,
|
|
||||||
{"executionLogs": executionLogs}
|
|
||||||
)
|
|
||||||
except Exception as logError:
|
except Exception as logError:
|
||||||
logger.error(f"Error saving execution log: {str(logError)}")
|
logger.error(f"Error saving execution log: {str(logError)}")
|
||||||
|
|
||||||
|
|
|
||||||
377
scripts/migrate_async_to_sync.py
Normal file
377
scripts/migrate_async_to_sync.py
Normal file
|
|
@ -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())
|
||||||
Loading…
Reference in a new issue