# Importações de bibliotecas padrão from typing import Annotated, Any, Sequence, List, Optional, Dict, Union from enum import Enum # Importações de bibliotecas de terceiros from fastapi import APIRouter, Depends from starlette import status from starlette.responses import Response from sqlalchemy import Row, RowMapping from pydantic import BaseModel, Field # Importações do seu próprio projeto from app.routers.dependencies import get_repository from app.database.RepositoryBase import RepositoryBase from app.database.RelationalTableRepository import RelationalTableRepository from app.rbac.permissions import verify_permissions from app.database.models import RbacUser from app.config import COLUNA from app.database.formatar_retorno_bd import formatters_map # Esquema para o corpo da requisição class FilterCondition(BaseModel): """ Representa uma condição de filtro individual. """ column: str = Field(..., description="Caminho completo da coluna (exemplo: 'relacao_setor.setor_nome').") value: Any = Field(..., description="Valor a ser usado no filtro.") operator: str = Field("==", description="Operador lógico (exemplo: '==', '!=', '<', '>', '>=', '<=').") logical: str = Field("AND", description="Operador lógico para combinações ('AND', 'OR' etc).") class FilterRequest(BaseModel): """ Representa a requisição contendo filtros dinâmicos. """ filters: Optional[List[FilterCondition]] = Field( None, description="Lista de condições de filtro. Cada condição contém coluna, operador, valor, e operador lógico." ) order_by: Optional[List[str]] = Field( None, description="Lista de colunas para ordenação dos registros." ) ascending: Optional[List[bool]] = Field( None, description="Lista de direções da ordenação. True para ascendente, False para descendente. " "Deve corresponder ao tamanho de 'order_by'." ) relationships: Optional[List[str]] = Field( None, description="Lista de nomes de relacionamentos para incluir na consulta." ) def create_dynamic_router( rota_prefix: str, create, request, id_many, id_one, update, updates, db_model, ativo: Optional = None, rota_tags: Optional[List[Union[str, Enum]]] = None, request_formatado: Optional = None, permissao_total: Optional[int] = None, permissao_endpoint: Optional[int] = None, permissao_setor: Optional[int] = None, permissao_especifica_inicial: Optional[int] = None, ordenacao: Optional[str] = None, formatter_keys: Optional[dict[str, str]] = None, related_info_append: Optional[List[Dict[str, Any]]] = None, # Dados extras para append related_info_add: Optional[List[Dict[str, Any]]] = None, # Dados extras para add related_info_extra_columns: Optional[List[Dict[str, Any]]] = None # Dados extras para ): # Verifica automaticamente se deve usar RelationalTableRepository use_complex_repo = bool(related_info_append or related_info_add or related_info_extra_columns) # Define o repositório com base na necessidade de relacionamentos complexos if use_complex_repo: repository_base = Annotated[ RelationalTableRepository[db_model], Depends(get_repository(db_model, RelationalTableRepository)), ] else: repository_base = Annotated[ RepositoryBase[db_model], Depends(get_repository(db_model, RepositoryBase)), ] router = APIRouter( prefix=rota_prefix, tags=rota_tags, responses={404: {"description": "Not found"}}, ) permissao_especifica_atual = permissao_especifica_inicial or 0 def get_permission_list(): """Retorna a lista de permissões atualizada e incrementa a permissão específica.""" nonlocal permissao_especifica_atual # Construir a lista de permissões baseada nos valores informados permission_list = [ permissao_total, permissao_endpoint, permissao_setor, permissao_especifica_atual if permissao_especifica_inicial is not None else None ] # Incrementa a permissão específica somente se foi inicialmente definida if permissao_especifica_inicial is not None: permissao_especifica_atual += 1 # Filtrar permissões nulas final_permissions = [perm for perm in permission_list if perm is not None] return final_permissions @router.post("/add_one", status_code=status.HTTP_201_CREATED, response_model=request, # dependencies=[Depends(verify_permissions(get_permission_list()))] ) async def create_one(data: create, repository: repository_base, _user: RbacUser = Depends(verify_permissions(get_permission_list()))) -> db_model: if use_complex_repo: resultado = await repository.create( data_one=data, db_data=db_model, related_info_append=related_info_append, related_info_add=related_info_add, related_info_extra_columns=related_info_extra_columns ) else: resultado = await repository.create(data_one=data) return resultado @router.post("/add_many", status_code=status.HTTP_201_CREATED, response_model=list[request]) async def create_many(datas: List[create], repository: repository_base, _user: RbacUser = Depends(verify_permissions(get_permission_list()))) -> ( list[db_model] | bool): if use_complex_repo: resultado = await repository.create_many( data=datas, db_data=db_model, return_models=True, related_info_append=related_info_append, related_info_add=related_info_add, related_info_extra_columns=related_info_extra_columns ) else: resultado = await repository.create_many(data=datas, return_models=True) return resultado permission_list_iguais = get_permission_list() # Obtém a lista de permissões para as funções get_all e get_filter if request_formatado and formatter_keys and "get_all" in formatter_keys: formatter_function = formatters_map[formatter_keys["get_all"]] @router.post("/get_all", status_code=status.HTTP_200_OK, response_model=list[request_formatado]) async def get_all(repository: repository_base, _user: RbacUser = Depends(verify_permissions(permission_list_iguais))) \ -> Sequence[Row[Any] | RowMapping | Any]: resultado = await repository.get_many_by_ids(coluna=COLUNA, order_by=ordenacao) formatado = formatter_function(resultado) return formatado @router.post("/get_filter", status_code=status.HTTP_200_OK, response_model=list[request_formatado]) async def get_all(repository: repository_base, filtro: Optional[FilterRequest] = None, _user: RbacUser = Depends(verify_permissions(permission_list_iguais))) -> ( Sequence)[Row[Any] | RowMapping | Any]: if not filtro: resultado = await repository.get_many_by_ids(coluna=COLUNA, order_by=ordenacao) formatado = formatter_function(resultado) # Chamando a função do repositório com os filtros e ordenação else: resultado = await repository.get_filter( coluna=COLUNA, filters=filtro.filters, order_by=filtro.order_by, ascending=filtro.ascending ) formatado = formatter_function(resultado) return formatado else: @router.post("/get_all", status_code=status.HTTP_200_OK, response_model=list[request]) async def get_all(repository: repository_base, _user: RbacUser = Depends(verify_permissions(permission_list_iguais))) -> ( Sequence)[Row[Any] | RowMapping | Any]: resultado = await repository.get_many_by_ids(coluna=COLUNA, order_by=ordenacao) return resultado @router.post("/get_filter", status_code=status.HTTP_200_OK, response_model=list[request]) async def get_all(repository: repository_base, filtro: Optional[FilterRequest] = None, _user: RbacUser = Depends(verify_permissions(permission_list_iguais))) -> ( Sequence)[Row[Any] | RowMapping | Any]: if not filtro: resultado = await repository.get_many_by_ids(coluna=COLUNA, order_by=ordenacao) # Chamando a função do repositório com os filtros e ordenação else: resultado = await repository.get_filter( coluna=COLUNA, filters=filtro.filters, order_by=filtro.order_by, ascending=filtro.ascending ) return resultado @router.post("/get_many", status_code=status.HTTP_200_OK, response_model=List[request]) async def get_many(data: id_many, repository: repository_base, _user: RbacUser = Depends(verify_permissions(get_permission_list()))) -> ( Sequence[Row[Any] | RowMapping | Any]): resultado = await repository.get_many_by_ids(uuids=data.uuids, coluna=COLUNA, order_by=ordenacao) return resultado @router.post("/get_one", status_code=status.HTTP_200_OK, response_model=request) async def get_one(data: id_one, repository: repository_base, _user: RbacUser = Depends(verify_permissions(get_permission_list()))) -> db_model: resultado = await repository.get_one_by_id(uuid=data.uuid, coluna=COLUNA) return resultado @router.put("/update_one", status_code=status.HTTP_201_CREATED, response_model=request) async def update_one(data: update, repository: repository_base, _user: RbacUser = Depends(verify_permissions(get_permission_list()))) -> db_model: if use_complex_repo: resultado = await repository.update_by_id( update=data, coluna=COLUNA, db_data=db_model, related_info_append=related_info_append, related_info_add=related_info_add, related_info_extra_columns=related_info_extra_columns ) else: resultado = await repository.update_by_id(update=data, coluna=COLUNA) return resultado if not use_complex_repo: @router.put("/update_many", status_code=status.HTTP_201_CREATED, response_model=List[request]) async def update_many(data: List[updates], repository: repository_base, _user: RbacUser = Depends(verify_permissions(get_permission_list()))) -> ( list[db_model] | db_model): resultado = await repository.update_many_by_ids(updates=data, coluna=COLUNA, return_models=True) return resultado else: # Não registramos a rota update_many, mas consumimos o efeito colateral da chamada # de get_permission_list() para que o contador seja incrementado. _loopPermissoes = get_permission_list() if ativo is not None: @router.put("/desativar", status_code=status.HTTP_201_CREATED, response_model=request) async def desativar(data: ativo, repository: repository_base, _user: RbacUser = Depends(verify_permissions(get_permission_list()))) -> db_model: resultado = await repository.desativar_registro(update=data, coluna=COLUNA) return resultado @router.put("/ativar", status_code=status.HTTP_201_CREATED, response_model=request) async def ativar(data: ativo, repository: repository_base, _user: RbacUser = Depends(verify_permissions([permissao_total]))) -> db_model: resultado = await repository.ativar_registro(update=data, coluna=COLUNA) return resultado else: # Não registramos a rota desativar, mas consumimos o efeito colateral da chamada # de get_permission_list() para que o contador seja incrementado. _loopPermissoes = get_permission_list() @router.delete("/delete_one", status_code=status.HTTP_204_NO_CONTENT) async def delete_one(data: id_one, repository: repository_base, _user: RbacUser = Depends(verify_permissions([permissao_total]))) -> Response: await repository.remove_by_id(uuid=data.uuid, coluna=COLUNA) return Response(status_code=status.HTTP_204_NO_CONTENT) @router.delete("/delete_many", status_code=status.HTTP_204_NO_CONTENT) async def delete_many(data: id_many, repository: repository_base, _user: RbacUser = Depends(verify_permissions([permissao_total]))) -> Response: await repository.remove_many_by_ids(uuids=data.uuids, coluna=COLUNA) return Response(status_code=status.HTTP_204_NO_CONTENT) return router