from typing import Any, Dict, List, Optional from fastapi import APIRouter, Request from core.sap import forward_to_sapb1 def _build_sap_params( skip: int, top: int, fields: Optional[str], filter: Optional[str], orderby: Optional[str] = None, ) -> Dict[str, Any]: sap_params: Dict[str, Any] = { "$skip": skip, "$top": top, "$inlinecount": "allpages", } if fields: sap_params["$select"] = fields.replace(" ", "") if filter: sap_params["$filter"] = filter if orderby: sap_params["$orderby"] = orderby return sap_params def _build_list_headers(top: int, filter: Optional[str]) -> Dict[str, str]: headers = {"B1S-PageSize": str(top)} if filter: headers["B1S-CaseInsensitive"] = "true" return headers def make_crud_router( resource: str, *, tags: List[str], key_field: Optional[str] = None, key_quoted: bool = False, supports_orderby: bool = False, supports_get_by_key: bool = True, supports_create: bool = True, supports_patch: bool = True, patch_headers: Optional[Dict[str, str]] = None, ) -> APIRouter: """ Genera el router list/get/create/update repetido antes en cada módulo de routers/ (mismo bloque de $skip/$top/$filter y de forward_to_sapb1, solo cambiaba el nombre del recurso OData). Centralizarlo evita que un copy-paste entre recursos deje, por ejemplo, un PATCH apuntando al endpoint_path de otro recurso. """ router = APIRouter(tags=tags) def key_expr(key: str) -> str: return f"{resource}('{key}')" if key_quoted else f"{resource}({key})" if supports_orderby: @router.get(f"/api/{resource}", name=f"list_{resource}") async def list_resource( skip: int = 0, top: int = 50, fields: Optional[str] = None, filter: Optional[str] = None, orderby: Optional[str] = None, ): sap_params = _build_sap_params(skip, top, fields, filter, orderby) return forward_to_sapb1( method="GET", endpoint_path=resource, params=sap_params, extra_headers=_build_list_headers(top, filter), ).json() else: @router.get(f"/api/{resource}", name=f"list_{resource}") async def list_resource( skip: int = 0, top: int = 50, fields: Optional[str] = None, filter: Optional[str] = None, ): sap_params = _build_sap_params(skip, top, fields, filter) return forward_to_sapb1( method="GET", endpoint_path=resource, params=sap_params, extra_headers=_build_list_headers(top, filter), ).json() if supports_get_by_key: @router.get(f"/api/{resource}/{{key}}", name=f"get_{resource}_by_key") async def get_by_key(key: str): return forward_to_sapb1( method="GET", endpoint_path=key_expr(key), ).json() if supports_create: @router.post(f"/api/{resource}", status_code=201, name=f"create_{resource}") async def create_resource(request: Request): data = await request.json() resp = forward_to_sapb1( method="POST", endpoint_path=resource, json_data=data, ) created = resp.json() return {key_field: created[key_field]} if supports_patch: @router.patch(f"/api/{resource}/{{key}}", status_code=204, name=f"update_{resource}") async def update_resource(key: str, request: Request): data = await request.json() forward_to_sapb1( method="PATCH", endpoint_path=key_expr(key), json_data=data, extra_headers=patch_headers, ) return router