from dataclasses import dataclass, field from typing import List, Optional, Dict, Any, Callable from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from typing import Union # First, let's create our category models class CategoryBase(BaseModel): id: str name: str description: Optional[str] = None class CategoryCreate(CategoryBase): parent_id: Optional[str] = None class CategoryResponse(CategoryBase): children: List['CategoryResponse'] = [] parent_id: Optional[str] = None # Category data structure for handling the hierarchy @dataclass class CategoryNode: id: str name: str description: Optional[str] parent_id: Optional[str] = None children: List['CategoryNode'] = field(default_factory=list) # Category Service for managing the hierarchy class CategoryService: def __init__(self): self.categories: Dict[str, CategoryNode] = {} def add_category(self, category: CategoryCreate) -> CategoryNode: node = CategoryNode( id=category.id, name=category.name, description=category.description, parent_id=category.parent_id ) self.categories[category.id] = node if category.parent_id and category.parent_id in self.categories: parent = self.categories[category.parent_id] parent.children.append(node) return node def get_category_tree(self, category_id: str) -> Optional[CategoryNode]: return self.categories.get(category_id) def get_category_path(self, category_id: str) -> List[CategoryNode]: path = [] current = self.categories.get(category_id) while current: path.append(current) current = self.categories.get(current.parent_id) if current.parent_id else None return list(reversed(path)) # Factory for creating category endpoints class CategoryEndpointFactory: def __init__(self, category_service: CategoryService): self.category_service = category_service def create_route_config(self, base_prefix: str) -> RouteFactoryConfig: endpoints = [ # Create category endpoint EndpointFactoryConfig( url_prefix=base_prefix, url_endpoint="/categories", url_of_endpoint=f"{base_prefix}/categories", endpoint="/categories", method="POST", summary="Create new category", description="Create a new category with optional parent", endpoint_function=self.create_category, request_model=CategoryCreate, response_model=CategoryResponse, is_auth_required=True ), # Get category tree endpoint EndpointFactoryConfig( url_prefix=base_prefix, url_endpoint="/categories/{category_id}", url_of_endpoint=f"{base_prefix}/categories/{{category_id}}", endpoint="/categories/{category_id}", method="GET", summary="Get category tree", description="Get category and its children", endpoint_function=self.get_category_tree, response_model=CategoryResponse, is_auth_required=True ), # Get category path endpoint EndpointFactoryConfig( url_prefix=base_prefix, url_endpoint="/categories/{category_id}/path", url_of_endpoint=f"{base_prefix}/categories/{{category_id}}/path", endpoint="/categories/{category_id}/path", method="GET", summary="Get category path", description="Get full path from root to this category", endpoint_function=self.get_category_path, response_model=List[CategoryResponse], is_auth_required=True ) ] return RouteFactoryConfig( name="categories", tags=["Categories"], prefix=base_prefix, endpoints=endpoints ) async def create_category(self, category: CategoryCreate) -> CategoryResponse: node = self.category_service.add_category(category) return self._convert_to_response(node) async def get_category_tree(self, category_id: str) -> CategoryResponse: node = self.category_service.get_category_tree(category_id) if not node: raise HTTPException(status_code=404, detail="Category not found") return self._convert_to_response(node) async def get_category_path(self, category_id: str) -> List[CategoryResponse]: path = self.category_service.get_category_path(category_id) if not path: raise HTTPException(status_code=404, detail="Category not found") return [self._convert_to_response(node) for node in path] def _convert_to_response(self, node: CategoryNode) -> CategoryResponse: return CategoryResponse( id=node.id, name=node.name, description=node.description, parent_id=node.parent_id, children=[self._convert_to_response(child) for child in node.children] ) # Usage example def create_category_router(base_prefix: str = "/api/v1") -> APIRouter: category_service = CategoryService() factory = CategoryEndpointFactory(category_service) route_config = factory.create_route_config(base_prefix) router = APIRouter( prefix=route_config.prefix, tags=route_config.tags ) for endpoint in route_config.endpoints: router.add_api_route( path=endpoint.endpoint, endpoint=endpoint.endpoint_function, methods=[endpoint.method], response_model=endpoint.response_model, summary=endpoint.summary, description=endpoint.description, **endpoint.extra_options ) return router