from fastapi import APIRouter, HTTPException, Depends, Path, Response from typing import List, Dict, Any from fastapi import status import inspect import importlib import os from pydantic import BaseModel import logging # Import auth module import modules.security.auth as auth # Import the attribute definition and helper functions from modules.shared.defAttributes import AttributeDefinition, getModelAttributes # Configure logger logger = logging.getLogger(__name__) # Create a response model for better documentation class AttributeResponse(BaseModel): """Response model for entity attributes""" attributes: List[AttributeDefinition] class Config: schema_extra = { "example": { "attributes": [ { "name": "username", "label": "Username", "type": "string", "required": True, "placeholder": "Please enter username", "editable": True, "visible": True, "order": 0 } ] } } def getModelClasses() -> Dict[str, Any]: """Dynamically get all model classes from all model modules""" modelClasses = {} # Get the interfaces directory path # Since we're in modules/routes/, we need to go up one level to modules/ then into interfaces/ interfaces_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'interfaces') # Find all model files for filename in os.listdir(interfaces_dir): if filename.endswith('Model.py'): # Convert filename to module name (e.g., gatewayModel.py -> gatewayModel) module_name = filename[:-3] # Import the module dynamically module = importlib.import_module(f'modules.interfaces.{module_name}') # Get all classes from the module for name, obj in inspect.getmembers(module): if inspect.isclass(obj) and issubclass(obj, BaseModel) and obj != BaseModel: modelClasses[name.lower()] = obj return modelClasses # Create a router for the attribute endpoints router = APIRouter( prefix="/api/attributes", tags=["Attributes"], responses={404: {"description": "Not found"}} ) @router.get("/{entityType}", response_model=AttributeResponse) async def get_entity_attributes( entityType: str = Path(..., description="Type of entity (e.g. prompt)"), currentUser: Dict[str, Any] = Depends(auth.getCurrentActiveUser) ): """ Retrieves the attribute definitions for a specific entity. This can be used for dynamic form generation. Parameters: - entityType: The type of entity to get attributes for (e.g., 'user', 'prompt') Returns: - A list of attribute definitions that can be used to generate forms """ # Determine preferred language of the user userLanguage = currentUser.get("language", "en") # Get model classes dynamically modelClasses = getModelClasses() # Check if entity type is known if entityType not in modelClasses: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Entity type '{entityType}' not found." ) # Get model class and derive attributes from it modelClass = modelClasses[entityType] attributes = getModelAttributes(modelClass, userLanguage) # Return only visible attributes return AttributeResponse(attributes=[attr for attr in attributes if attr.visible]) @router.options("/{entityType}") async def options_entity_attributes( entityType: str = Path(..., description="Type of entity (e.g. prompt)") ): """Handle OPTIONS request for CORS preflight""" return Response(status_code=200)